Merge "Cancel touchscreen gestures from Dispatcher when display rotates" into sc-dev
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 307e21c..4b64203 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -60,9 +60,6 @@
]
},
{
- "name": "libsurfaceflinger_unittest"
- },
- {
"name": "CtsGraphicsTestCases",
"options": [
{
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index cedff0b..79419d3 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -323,9 +323,6 @@
static const char* k_funcgraphProcPath =
"options/funcgraph-proc";
-static const char* k_funcgraphFlatPath =
- "options/funcgraph-flat";
-
static const char* k_ftraceFilterPath =
"set_ftrace_filter";
@@ -703,7 +700,6 @@
ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true);
ok &= setKernelOptionEnable(k_funcgraphCpuPath, true);
ok &= setKernelOptionEnable(k_funcgraphProcPath, true);
- ok &= setKernelOptionEnable(k_funcgraphFlatPath, true);
// Set the requested filter functions.
ok &= truncateFile(k_ftraceFilterPath);
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreport/OWNERS
+++ b/cmds/bugreport/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreportz/OWNERS
+++ b/cmds/bugreportz/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 336c5a2..25e6dc9 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1641,6 +1641,10 @@
MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
} else {
RunCommand("LSMOD", {"lsmod"});
+ RunCommand("MODULES INFO",
+ {"sh", "-c", "cat /proc/modules | cut -d' ' -f1 | "
+ " while read MOD ; do echo modinfo:$MOD ; modinfo $MOD ; "
+ "done"}, CommandOptions::AS_ROOT);
}
if (android::base::GetBoolProperty("ro.logd.kernel", false)) {
@@ -1692,6 +1696,12 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh);
+ // The dump mechanism in connectivity is refactored due to modularization work. Connectivity can
+ // only register with a default priority(NORMAL priority). Dumpstate has to call connectivity
+ // dump with priority parameters to dump high priority information.
+ RunDumpsys("SERVICE HIGH connectivity", {"connectivity", "--dump-priority", "HIGH"},
+ CommandOptions::WithTimeout(10).Build());
+
RunCommand("SYSTEM PROPERTIES", {"getprop"});
RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
@@ -1960,6 +1970,8 @@
RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"vcn_management"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
if (include_sensitive_info) {
// Carrier apps' services will be dumped below in dumpsys activity service all-non-platform.
RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
@@ -3183,6 +3195,11 @@
// Since we do not have user consent to share the bugreport it does not get
// copied over to the calling app but remains in the internal directory from
// where the user can manually pull it.
+ std::string final_path = GetPath(".zip");
+ bool copy_succeeded = android::os::CopyFileToFile(path_, final_path);
+ if (copy_succeeded) {
+ android::os::UnlinkAndLogOnError(path_);
+ }
return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
}
// Unknown result; must be a programming error.
diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS
index 2a9b681..97a63ca 100644
--- a/cmds/dumpsys/OWNERS
+++ b/cmds/dumpsys/OWNERS
@@ -1,4 +1,8 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
+
+# for ServiceManager mock
+per-file dumpsys_test.cpp=smoreland@google.com
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 39af7df..c9d2dbb 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -59,6 +59,7 @@
MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
MOCK_METHOD1(isDeclared, bool(const String16&));
MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
+ MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index a546236..3f180d9 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -47,6 +47,9 @@
"libutils",
"server_configurable_flags",
],
+ static_libs: [
+ "libasync_safe",
+ ],
export_shared_lib_headers: [
"libbinder",
],
@@ -250,6 +253,7 @@
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libotapreoptparameters",
],
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index fc745d0..d6807ff 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -9,3 +9,4 @@
ngeoffray@google.com
rpl@google.com
toddke@google.com
+patb@google.com
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 0cf50a3..204953c 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -36,6 +36,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
#include <cutils/fs.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
@@ -727,7 +728,8 @@
if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
if (errno != EWOULDBLOCK) {
- PLOG(WARNING) << "Error locking profile " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error locking profile %s: %d",
+ package_name.c_str(), errno);
}
// This implies that the app owning this profile is running
// (and has acquired the lock).
@@ -735,13 +737,15 @@
// The app never acquires the lock for the reference profiles of primary apks.
// Only dex2oat from installd will do that. Since installd is single threaded
// we should not see this case. Nevertheless be prepared for it.
- PLOG(WARNING) << "Failed to flock " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Failed to flock %s: %d",
+ package_name.c_str(), errno);
return false;
}
bool truncated = ftruncate(out_fd.get(), 0) == 0;
if (!truncated) {
- PLOG(WARNING) << "Could not truncate " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Could not truncate %s: %d",
+ package_name.c_str(), errno);
}
// Copy over data.
@@ -755,7 +759,8 @@
write(out_fd.get(), buffer, bytes);
}
if (flock(out_fd.get(), LOCK_UN) != 0) {
- PLOG(WARNING) << "Error unlocking profile " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error unlocking profile %s: %d",
+ package_name.c_str(), errno);
}
// Use _exit since we don't want to run the global destructors in the child.
// b/62597429
@@ -1513,7 +1518,8 @@
// Validate the path structure.
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
_exit(kSecondaryDexDexoptAnalyzerSkippedValidatePath);
}
@@ -1809,7 +1815,8 @@
drop_capabilities(uid);
if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
- PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "flock(%s) failed",
+ out_oat.path().c_str());
_exit(DexoptReturnCodes::kFlock);
}
@@ -1904,7 +1911,8 @@
const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
_exit(kReconcileSecondaryDexValidationError);
}
@@ -1917,7 +1925,8 @@
case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError);
case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError);
default:
- LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Unexpected result from check_secondary_dex_access: %d", access_check);
_exit(kReconcileSecondaryDexValidationError);
}
@@ -1930,7 +1939,7 @@
std::string error_msg;
if (!create_secondary_dex_oat_layout(
dex_path,isas[i], oat_dir, oat_isa_dir, oat_path, &error_msg)) {
- LOG(ERROR) << error_msg;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str());
_exit(kReconcileSecondaryDexValidationError);
}
@@ -1957,7 +1966,8 @@
result = rmdir_if_empty(oat_dir) && result;
}
if (!result) {
- PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
}
_exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
}
@@ -2030,7 +2040,8 @@
pipe_read.reset();
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
_exit(DexoptReturnCodes::kHashValidatePath);
}
@@ -2041,6 +2052,8 @@
_exit(0);
}
PLOG(ERROR) << "Failed to open secondary dex " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Failed to open secondary dex %s: %d", dex_path.c_str(), errno);
_exit(DexoptReturnCodes::kHashOpenPath);
}
@@ -2053,7 +2066,8 @@
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
- PLOG(ERROR) << "Failed to read secondary dex " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Failed to read secondary dex %s: %d", dex_path.c_str(), errno);
_exit(DexoptReturnCodes::kHashReadDex);
}
diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h
index 3e2f815..88801ca 100644
--- a/cmds/installd/file_parsing.h
+++ b/cmds/installd/file_parsing.h
@@ -19,18 +19,14 @@
#include <fstream>
#include <functional>
-#include <string>
+#include <string_view>
+#include "android-base/unique_fd.h"
namespace android {
namespace installd {
-bool ParseFile(const std::string& strFile, std::function<bool (const std::string&)> parse) {
- std::ifstream input_stream(strFile);
-
- if (!input_stream.is_open()) {
- return false;
- }
-
+template<typename Func>
+bool ParseFile(std::istream& input_stream, Func parse) {
while (!input_stream.eof()) {
// Read the next line.
std::string line;
@@ -54,6 +50,15 @@
return true;
}
+template<typename Func>
+bool ParseFile(std::string_view str_file, Func parse) {
+ std::ifstream ifs(str_file);
+ if (!ifs.is_open()) {
+ return false;
+ }
+ return ParseFile(ifs, parse);
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index ed31ad9..6aa32b8 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -26,6 +26,7 @@
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/stat.h>
+#include <sys/mman.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -36,6 +37,7 @@
#include <log/log.h>
#include <private/android_filesystem_config.h>
+#include "android-base/file.h"
#include "dexopt.h"
#include "file_parsing.h"
#include "globals.h"
@@ -195,38 +197,63 @@
// export NAME VALUE
// For simplicity, don't respect string quotation. The values we are interested in can be
// encoded without them.
- // init.environ.rc and etc/classpath have the same format for
- // environment variable exports and can be matched by the same regex.
+ //
+ // init.environ.rc and derive_classpath all have the same format for
+ // environment variable exports (since they are all meant to be read by
+ // init) and can be matched by the same regex.
+
+ std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+ auto parse_results = [&](auto& input) {
+ ParseFile(input, [&](const std::string& line) {
+ std::smatch export_match;
+ if (!std::regex_match(line, export_match, export_regex)) {
+ return true;
+ }
+
+ if (export_match.size() != 3) {
+ return true;
+ }
+
+ std::string name = export_match[1].str();
+ std::string value = export_match[2].str();
+
+ system_properties_.SetProperty(name, value);
+
+ return true;
+ });
+ };
+
// TODO Just like with the system-properties above we really should have
// common code between init and otapreopt to deal with reading these
// things. See b/181182967
+ // There have been a variety of places the various env-vars have been
+ // over the years. Expand or reduce this list as needed.
static constexpr const char* kEnvironmentVariableSources[] = {
- "/init.environ.rc", "/etc/classpath"
+ "/init.environ.rc",
};
-
- std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+ // First get everything from the static files.
for (const char* env_vars_file : kEnvironmentVariableSources) {
- bool parse_result = ParseFile(env_vars_file, [&](const std::string& line) {
- std::smatch export_match;
- if (!std::regex_match(line, export_match, export_regex)) {
- return true;
- }
-
- if (export_match.size() != 3) {
- return true;
- }
-
- std::string name = export_match[1].str();
- std::string value = export_match[2].str();
-
- system_properties_.SetProperty(name, value);
-
- return true;
- });
- if (!parse_result) {
- return false;
- }
+ parse_results(env_vars_file);
}
+
+ // Next get everything from derive_classpath, since we're already in the
+ // chroot it will get the new versions of any dependencies.
+ {
+ android::base::unique_fd fd(memfd_create("derive_classpath_temp", MFD_CLOEXEC));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Unable to create fd for derive_classpath";
+ return false;
+ }
+ std::string memfd_file = StringPrintf("/proc/%d/fd/%d", getpid(), fd.get());
+ std::string error_msg;
+ if (!Exec({"/apex/com.android.sdkext/bin/derive_classpath", memfd_file}, &error_msg)) {
+ PLOG(ERROR) << "Running derive_classpath failed: " << error_msg;
+ return false;
+ }
+ std::ifstream ifs(memfd_file);
+ parse_results(ifs);
+ }
+
if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) {
return false;
}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 83f01de..c62734a 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -275,6 +275,7 @@
static constexpr const std::string_view kRequiredApexs[] = {
"com.android.art",
"com.android.runtime",
+ "com.android.sdkext", // For derive_classpath
};
std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false };
DIR* apex_dir = opendir("/apex");
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index f67ab81..7082017 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -20,6 +20,7 @@
"libcutils",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
@@ -44,6 +45,7 @@
"server_configurable_flags",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
@@ -84,6 +86,7 @@
"server_configurable_flags",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
@@ -124,6 +127,7 @@
"server_configurable_flags",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index d5110f6..2722e21 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -417,7 +417,7 @@
}
}
out << "-->" << std::endl;
- out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlags::HALS_ONLY);
+ out << vintf::toXml(manifest, vintf::SerializeFlags::HALS_ONLY);
}
std::string ListCommand::INIT_VINTF_NOTES{
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 7c1ca91..6f08f74 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -47,8 +47,6 @@
using ::android::hardware::Void;
using android::vintf::Arch;
using android::vintf::CompatibilityMatrix;
-using android::vintf::gCompatibilityMatrixConverter;
-using android::vintf::gHalManifestConverter;
using android::vintf::HalManifest;
using android::vintf::Transport;
using android::vintf::VintfObject;
@@ -510,7 +508,7 @@
std::string error;
vintf::HalManifest m;
- EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str(), &error))
+ EXPECT_EQ(true, vintf::fromXml(&m, out.str(), &error))
<< "--init-vintf does not emit valid HAL manifest: " << error;
}
@@ -775,10 +773,10 @@
auto deviceMatrix = std::make_shared<CompatibilityMatrix>();
auto frameworkMatrix = std::make_shared<CompatibilityMatrix>();
- ASSERT_TRUE(gHalManifestConverter(deviceManifest.get(), deviceManifestXml));
- ASSERT_TRUE(gHalManifestConverter(frameworkManifest.get(), frameworkManifestXml));
- ASSERT_TRUE(gCompatibilityMatrixConverter(deviceMatrix.get(), deviceMatrixXml));
- ASSERT_TRUE(gCompatibilityMatrixConverter(frameworkMatrix.get(), frameworkMatrixXml));
+ ASSERT_TRUE(fromXml(deviceManifest.get(), deviceManifestXml));
+ ASSERT_TRUE(fromXml(frameworkManifest.get(), frameworkManifestXml));
+ ASSERT_TRUE(fromXml(deviceMatrix.get(), deviceMatrixXml));
+ ASSERT_TRUE(fromXml(frameworkMatrix.get(), frameworkMatrixXml));
ON_CALL(*mockList, getDeviceManifest()).WillByDefault(Return(deviceManifest));
ON_CALL(*mockList, getDeviceMatrix()).WillByDefault(Return(deviceMatrix));
@@ -964,7 +962,7 @@
" </hal>\n"
"</manifest>";
auto manifest = std::make_shared<HalManifest>();
- EXPECT_TRUE(gHalManifestConverter(manifest.get(), mockManifestXml));
+ EXPECT_TRUE(fromXml(manifest.get(), mockManifestXml));
EXPECT_CALL(*mockList, getDeviceManifest())
.Times(AnyNumber())
.WillRepeatedly(Return(manifest));
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 2f55249..90db509 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -58,22 +58,34 @@
return false;
}
-static bool isVintfDeclared(const std::string& name) {
- size_t firstSlash = name.find('/');
- size_t lastDot = name.rfind('.', firstSlash);
- if (firstSlash == std::string::npos || lastDot == std::string::npos) {
- LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. "
- << "some.package.foo.IFoo/default) but got: " << name;
- return false;
- }
- const std::string package = name.substr(0, lastDot);
- const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
- const std::string instance = name.substr(firstSlash+1);
+struct AidlName {
+ std::string package;
+ std::string iface;
+ std::string instance;
- bool found = forEachManifest([&] (const ManifestWithDescription& mwd) {
- if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
+ static bool fill(const std::string& name, AidlName* aname) {
+ size_t firstSlash = name.find('/');
+ size_t lastDot = name.rfind('.', firstSlash);
+ if (firstSlash == std::string::npos || lastDot == std::string::npos) {
+ LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. "
+ << "some.package.foo.IFoo/default) but got: " << name;
+ return false;
+ }
+ aname->package = name.substr(0, lastDot);
+ aname->iface = name.substr(lastDot + 1, firstSlash - lastDot - 1);
+ aname->instance = name.substr(firstSlash + 1);
+ return true;
+ }
+};
+
+static bool isVintfDeclared(const std::string& name) {
+ AidlName aname;
+ if (!AidlName::fill(name, &aname)) return false;
+
+ bool found = forEachManifest([&](const ManifestWithDescription& mwd) {
+ if (mwd.manifest->hasAidlInstance(aname.package, aname.iface, aname.instance)) {
LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
- return true;
+ return true; // break
}
return false; // continue
});
@@ -81,13 +93,34 @@
if (!found) {
// Although it is tested, explicitly rebuilding qualified name, in case it
// becomes something unexpected.
- LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
- << " in the VINTF manifest.";
+ LOG(ERROR) << "Could not find " << aname.package << "." << aname.iface << "/"
+ << aname.instance << " in the VINTF manifest.";
}
return found;
}
+static std::optional<std::string> getVintfUpdatableApex(const std::string& name) {
+ AidlName aname;
+ if (!AidlName::fill(name, &aname)) return std::nullopt;
+
+ std::optional<std::string> updatableViaApex;
+
+ forEachManifest([&](const ManifestWithDescription& mwd) {
+ mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+ if (manifestInstance.format() != vintf::HalFormat::AIDL) return true;
+ if (manifestInstance.package() != aname.package) return true;
+ if (manifestInstance.interface() != aname.iface) return true;
+ if (manifestInstance.instance() != aname.instance) return true;
+ updatableViaApex = manifestInstance.updatableViaApex();
+ return false; // break (libvintf uses opposite convention)
+ });
+ return false; // continue
+ });
+
+ return updatableViaApex;
+}
+
static std::vector<std::string> getVintfInstances(const std::string& interface) {
size_t lastDot = interface.rfind('.');
if (lastDot == std::string::npos) {
@@ -388,6 +421,22 @@
return Status::ok();
}
+Status ServiceManager::updatableViaApex(const std::string& name,
+ std::optional<std::string>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ if (!mAccess->canFind(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ *outReturn = std::nullopt;
+
+#ifndef VENDORSERVICEMANAGER
+ *outReturn = getVintfUpdatableApex(name);
+#endif
+ return Status::ok();
+}
+
void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found) {
@@ -432,7 +481,12 @@
name.c_str());
std::thread([=] {
- (void)base::SetProperty("ctl.interface_start", "aidl/" + name);
+ if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
+ LOG(INFO) << "Tried to start aidl service " << name
+ << " as a lazy service, but was unable to. Usually this happens when a "
+ "service is not installed, but if the service is intended to be used as a "
+ "lazy service, then it may be configured incorrectly.";
+ }
}).detach();
}
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index c089115..4f23c21 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -46,6 +46,8 @@
binder::Status isDeclared(const std::string& name, bool* outReturn) override;
binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
+ binder::Status updatableViaApex(const std::string& name,
+ std::optional<std::string>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) override;
binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 8d1bf99..fb7d09c 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -739,6 +739,9 @@
* skipping frames in an image with such frames may not produce the correct
* results.
*
+ * Only supported by {@link ANDROID_BITMAP_FORMAT_RGBA_8888} and
+ * {@link ANDROID_BITMAP_FORMAT_RGBA_F16}.
+ *
* @param decoder an {@link AImageDecoder} object.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure.
@@ -747,6 +750,8 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
* represents an image that is not animated (see
* {@link AImageDecoder_isAnimated}) or the AImageDecoder is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE): The requested
+ * {@link AndroidBitmapFormat} does not support animation.
* - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The input appears
* to be truncated. The client must call {@link AImageDecoder_rewind}
* before calling {@link AImageDecoder_decodeImage} again.
@@ -832,9 +837,11 @@
* is the current frame.
*
* If the image only has one frame, this will fill the {@link
- * AImageDecoderFrameInfo} with the encoded info, if any, or reasonable
+ * AImageDecoderFrameInfo} with the encoded info and reasonable
* defaults.
*
+ * If {@link AImageDecoder_advanceFrame} succeeded, this will succeed as well.
+ *
* @param decoder Opaque object representing the decoder.
* @param info Opaque object to hold frame information. On success, will be
* filled with information regarding the current frame.
@@ -856,7 +863,7 @@
* Introduced in API 31.
*
* Errors:
- * - returns 0 if |info| is null.
+ * - returns {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null.
*/
int64_t AImageDecoderFrameInfo_getDuration(
const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
@@ -891,19 +898,25 @@
*
* Introduced in API 31.
*
- * Note that this may differ from whether the composed frame has
- * alpha. If this frame does not fill the entire image dimensions
- * (see {@link AImageDecoderFrameInfo_getFrameRect}) or it blends
- * with an opaque frame, for example, the composed frame’s alpha
- * may not match. It is also conservative; for example, if a color
- * index-based frame has a color with alpha but does not use it,
- * this will still return true.
+ * Unless this frame is independent (see {@link AImageDecoder_decodeImage}),
+ * a single call to {@link AImageDecoder_decodeImage} will decode an updated
+ * rectangle of pixels and then blend it with the existing pixels in the
+ * |pixels| buffer according to {@link AImageDecoderFrameInfo_getBlendOp}. This
+ * method returns whether the updated rectangle has alpha, prior to blending.
+ * The return value is conservative; for example, if a color-index-based frame
+ * has a color with alpha but does not use it, this will still return true.
*
* This, along with other information in AImageDecoderFrameInfo,
* can be useful for determining whether a frame is independent, but
* the decoder handles blending frames, so a simple
* sequential client does not need this.
*
+ * Note that this may differ from whether the composed frame (that is, the
+ * resulting image after blending) has alpha. If this frame does not fill the
+ * entire image dimensions (see {@link AImageDecoderFrameInfo_getFrameRect})
+ * or it blends with an opaque frame, for example, the composed frame’s alpha
+ * may not match.
+ *
* Errors:
* - returns false if |info| is null.
*/
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index c349024..059bc41 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -133,6 +133,9 @@
* ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
* including the updates in a transaction was presented.
*
+ * Buffers which are replaced or removed from the scene in the transaction invoking
+ * this callback may be reused after this point.
+ *
* \param context Optional context provided by the client that is passed into
* the callback.
*
@@ -147,6 +150,35 @@
typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
__INTRODUCED_IN(29);
+
+/**
+ * The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates
+ * are ready to be presented. This callback will be invoked before the
+ * ASurfaceTransaction_OnComplete callback.
+ *
+ * This callback does not mean buffers have been released! It simply means that any new
+ * transactions applied will not overwrite the transaction for which we are receiving
+ * a callback and instead will be included in the next frame. If you are trying to avoid
+ * dropping frames (overwriting transactions), and unable to use timestamps (Which provide
+ * a more efficient solution), then this method provides a method to pace your transaction
+ * application.
+ *
+ * \param context Optional context provided by the client that is passed into the callback.
+ *
+ * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the callback.
+ * Present and release fences are not available for this callback. Querying them using
+ * ASurfaceTransactionStats_getPresentFenceFd and ASurfaceTransactionStats_getPreviousReleaseFenceFd
+ * will result in failure.
+ *
+ * THREADING
+ * The transaction committed callback can be invoked on any thread.
+ *
+ * Available since API level 31.
+ */
+typedef void (*ASurfaceTransaction_OnCommit)(void* context, ASurfaceTransactionStats* stats)
+ __INTRODUCED_IN(31);
+
/**
* Returns the timestamp of when the frame was latched by the framework. Once a frame is
* latched by the framework, it is presented at the following hardware vsync.
@@ -161,6 +193,8 @@
* The recipient of the callback takes ownership of the fence and is responsible for closing
* it. If a device does not support present fences, a -1 will be returned.
*
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
* Available since API level 29.
*/
int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
@@ -218,6 +252,8 @@
* The client must ensure that all pending refs on a buffer are released before attempting to reuse
* this buffer, otherwise synchronization errors may occur.
*
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
* Available since API level 29.
*/
int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
@@ -236,6 +272,16 @@
ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
/**
+ * Sets the callback that will be invoked when the updates from this transaction are applied and are
+ * ready to be presented. This callback will be invoked before the ASurfaceTransaction_OnComplete
+ * callback.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* transaction, void* context,
+ ASurfaceTransaction_OnCommit func) __INTRODUCED_IN(31);
+
+/**
* Reparents the \a surface_control from its old parent to the \a new_parent surface control.
* Any children of the reparented \a surface_control will remain children of the \a surface_control.
*
@@ -322,6 +368,11 @@
* enum.
*
* Available since API level 29.
+ *
+ * @deprecated Use setCrop, setPosition, setBufferTransform, and setScale instead. Those functions
+ * provide well defined behavior and allows for more control by the apps. It also allows the caller
+ * to set different properties at different times, instead of having to specify all the desired
+ * properties at once.
*/
void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, const ARect& source,
@@ -490,6 +541,9 @@
* callback timings, and changes to the time interval at which the system releases buffers back to
* the application.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
* value that indicates the app will accept the system's choice for the display frame rate, which is
* the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
@@ -498,11 +552,12 @@
*
* \param compatibility The frame rate compatibility of this surface. The compatibility value may
* influence the system's choice of display frame rate. To specify a compatibility use the
- * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. This parameter is ignored when frameRate is 0.
*
- * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
- * A seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * surface should be seamless. A seamless transition is one that doesn't have any visual
+ * interruptions, such as a black screen for a second or two. See the
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_* values. This parameter is ignored when frameRate is 0.
*
* Available since API level 31.
*/
diff --git a/include/input/Input.h b/include/input/Input.h
index bb5ca0e..d4defa8 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -70,6 +70,14 @@
*/
AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
+ /**
+ * This flag indicates that the event will not cause a focus change if it is directed to an
+ * unfocused window, even if it an ACTION_DOWN. This is typically used with pointer
+ * gestures to allow the user to direct gestures to an unfocused window without bringing it
+ * into focus.
+ */
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
+
/* Motion event is inconsistent with previously sent motion events. */
AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
};
@@ -318,6 +326,12 @@
*/
constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
+/**
+ * Invalid value for display size. Used when display size isn't available for an event or doesn't
+ * matter. This is just a constant 0 so that it has no effect if unused.
+ */
+constexpr int32_t AMOTION_EVENT_INVALID_DISPLAY_SIZE = 0;
+
/*
* Pointer coordinate data.
*/
@@ -360,6 +374,8 @@
return getAxisValue(AMOTION_EVENT_AXIS_Y);
}
+ vec2 getXYValue() const { return vec2(getX(), getY()); }
+
#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
@@ -548,6 +564,8 @@
void setCursorPosition(float x, float y);
+ int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; }
+
static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
inline nsecs_t getDownTime() const { return mDownTime; }
@@ -570,8 +588,17 @@
inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
+ /**
+ * The actual raw pointer coords: whatever comes from the input device without any external
+ * transforms applied.
+ */
const PointerCoords* getRawPointerCoords(size_t pointerIndex) const;
+ /**
+ * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw"
+ * transform because many apps (incorrectly) assumed that raw == oriented-screen-space.
+ * "compat raw" is raw coordinates with screen rotation applied.
+ */
float getRawAxisValue(int32_t axis, size_t pointerIndex) const;
inline float getRawX(size_t pointerIndex) const {
@@ -634,9 +661,18 @@
return mSampleEventTimes[historicalIndex];
}
+ /**
+ * The actual raw pointer coords: whatever comes from the input device without any external
+ * transforms applied.
+ */
const PointerCoords* getHistoricalRawPointerCoords(
size_t pointerIndex, size_t historicalIndex) const;
+ /**
+ * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw"
+ * transform because many apps (incorrectly) assumed that raw == oriented-screen-space.
+ * "compat raw" is raw coordinates with screen rotation applied.
+ */
float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const;
@@ -704,9 +740,9 @@
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float rawXCursorPosition,
- float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
- size_t pointerCount, const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords);
+ float rawYCursorPosition, int32_t displayWidth, int32_t displayHeight,
+ nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
void copyFrom(const MotionEvent* other, bool keepHistory);
@@ -718,10 +754,14 @@
void scale(float globalScaleFactor);
- // Apply 3x3 perspective matrix transformation.
+ // Set 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
void transform(const std::array<float, 9>& matrix);
+ // Apply 3x3 perspective matrix transformation only to content (do not modify mTransform).
+ // Matrix is in row-major form and compatible with SkMatrix.
+ void applyTransform(const std::array<float, 9>& matrix);
+
#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
@@ -759,6 +799,8 @@
float mYPrecision;
float mRawXCursorPosition;
float mRawYCursorPosition;
+ int32_t mDisplayWidth;
+ int32_t mDisplayHeight;
nsecs_t mDownTime;
Vector<PointerProperties> mPointerProperties;
std::vector<nsecs_t> mSampleEventTimes;
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 712adfa..1fec080 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -101,7 +101,7 @@
};
enum class InputDeviceLightType : int32_t {
- SINGLE = 0,
+ MONO = 0,
PLAYER_ID = 1,
RGB = 2,
MULTI_COLOR = 3,
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 898d1a9..360dfbf 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -136,6 +136,8 @@
float yPrecision;
float xCursorPosition;
float yCursorPosition;
+ int32_t displayWidth;
+ int32_t displayHeight;
uint32_t pointerCount;
uint32_t empty3;
/**
@@ -227,7 +229,7 @@
InputChannel(const InputChannel& other)
: mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
- virtual ~InputChannel();
+ ~InputChannel() override;
/**
* Create a pair of input channels.
* The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -353,8 +355,9 @@
int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
- uint32_t pointerCount, const PointerProperties* pointerProperties,
+ float yCursorPosition, int32_t displayWidth, int32_t displayHeight,
+ nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
/* Publishes a focus event to the input channel.
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 36097d6..121be6d 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -168,6 +168,10 @@
// Transform applied to individual windows.
ui::Transform transform;
+ // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
+ int32_t displayWidth = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
+ int32_t displayHeight = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
+
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
diff --git a/include/input/LatencyStatistics.h b/include/input/LatencyStatistics.h
deleted file mode 100644
index bd86266..0000000
--- a/include/input/LatencyStatistics.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_INPUT_STATISTICS_H
-#define _UI_INPUT_STATISTICS_H
-
-#include <android-base/chrono_utils.h>
-
-#include <stddef.h>
-
-namespace android {
-
-class LatencyStatistics {
-private:
- /* Minimum sample recorded */
- float mMin;
- /* Maximum sample recorded */
- float mMax;
- /* Sum of all samples recorded */
- float mSum;
- /* Sum of all the squares of samples recorded */
- float mSum2;
- /* Count of all samples recorded */
- size_t mCount;
- /* The last time statistics were reported */
- std::chrono::steady_clock::time_point mLastReportTime;
- /* Statistics Report Frequency */
- const std::chrono::seconds mReportPeriod;
-
-public:
- LatencyStatistics(std::chrono::seconds period);
-
- void addValue(float);
- void reset();
- bool shouldReport();
-
- float getMean();
- float getMin();
- float getMax();
- float getStDev();
- size_t getCount();
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_STATISTICS_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 1800481..be260e8 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -112,7 +112,7 @@
"PersistableBundle.cpp",
"ProcessState.cpp",
"RpcAddress.cpp",
- "RpcConnection.cpp",
+ "RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
"Static.cpp",
@@ -189,6 +189,9 @@
// Only check our headers
"--header-filter=^.*frameworks/native/libs/binder/.*.h$",
],
+ tidy_checks: [
+ "-performance-no-int-to-ptr",
+ ],
tidy_checks_as_errors: [
// Explicitly list the checks that should not occur in this module.
"abseil-*",
@@ -196,20 +199,9 @@
"bugprone-*",
"cert-*",
"clang-analyzer-*",
- "-clang-analyzer-core.CallAndMessage",
- "-clang-analyzer-core.uninitialized.Assign",
- "-clang-analyzer-unix.Malloc",
- "-clang-analyzer-deadcode.DeadStores",
- "-clang-analyzer-optin.cplusplus.UninitializedObject",
"google-*",
- "-google-readability-*",
- "-google-runtime-references",
"misc-*",
- "-misc-no-recursion",
- "-misc-redundant-expression",
- "-misc-unused-using-decls",
"performance*",
- "-performance-no-int-to-ptr",
"portability*",
],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index d964d25..d5bdd1c 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -29,6 +29,16 @@
namespace android {
+// Service implementations inherit from BBinder and IBinder, and this is frozen
+// in prebuilts.
+#ifdef __LP64__
+static_assert(sizeof(IBinder) == 24);
+static_assert(sizeof(BBinder) == 40);
+#else
+static_assert(sizeof(IBinder) == 12);
+static_assert(sizeof(BBinder) == 20);
+#endif
+
// ---------------------------------------------------------------------------
IBinder::IBinder()
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index fdcf94a..1dcb94c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -21,7 +21,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <binder/Stability.h>
#include <cutils/compiler.h>
#include <utils/Log.h>
@@ -136,15 +136,15 @@
return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
}
-sp<BpBinder> BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) {
- LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection");
+sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, const RpcAddress& address) {
+ LOG_ALWAYS_FATAL_IF(session == nullptr, "BpBinder::create null session");
// These are not currently tracked, since there is no UID or other
// identifier to track them with. However, if similar functionality is
- // needed, connection objects keep track of all BpBinder objects on a
- // per-connection basis.
+ // needed, session objects keep track of all BpBinder objects on a
+ // per-session basis.
- return sp<BpBinder>::make(SocketHandle{connection, address});
+ return sp<BpBinder>::make(RpcHandle{session, address});
}
BpBinder::BpBinder(Handle&& handle)
@@ -165,20 +165,20 @@
IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
}
-BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) {
- LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object");
+BpBinder::BpBinder(RpcHandle&& handle) : BpBinder(Handle(handle)) {
+ LOG_ALWAYS_FATAL_IF(rpcSession() == nullptr, "BpBinder created w/o session object");
}
bool BpBinder::isRpcBinder() const {
- return std::holds_alternative<SocketHandle>(mHandle);
+ return std::holds_alternative<RpcHandle>(mHandle);
}
const RpcAddress& BpBinder::rpcAddress() const {
- return std::get<SocketHandle>(mHandle).address;
+ return std::get<RpcHandle>(mHandle).address;
}
-const sp<RpcConnection>& BpBinder::rpcConnection() const {
- return std::get<SocketHandle>(mHandle).connection;
+const sp<RpcSession>& BpBinder::rpcSession() const {
+ return std::get<RpcHandle>(mHandle).session;
}
int32_t BpBinder::binderHandle() const {
@@ -273,7 +273,7 @@
status_t status;
if (CC_UNLIKELY(isRpcBinder())) {
- status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags);
+ status = rpcSession()->transact(rpcAddress(), code, data, reply, flags);
} else {
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
@@ -479,7 +479,7 @@
{
ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
if (CC_UNLIKELY(isRpcBinder())) {
- (void)rpcConnection()->sendDecStrong(rpcAddress());
+ (void)rpcSession()->sendDecStrong(rpcAddress());
return;
}
IF_ALOGV() {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 406bd54..c605e67 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -29,8 +29,6 @@
#include <utils/SystemClock.h>
#include <utils/threads.h>
-#include <private/binder/binder_module.h>
-
#include <atomic>
#include <errno.h>
#include <inttypes.h>
@@ -43,6 +41,7 @@
#include <unistd.h>
#include "Static.h"
+#include "binder_module.h"
#if LOG_NDEBUG
@@ -90,6 +89,8 @@
"BR_DEAD_BINDER",
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
"BR_FAILED_REPLY",
+ "BR_FROZEN_REPLY",
+ "BR_ONEWAY_SPAM_SUSPECT",
"BR_TRANSACTION_SEC_CTX",
};
@@ -488,14 +489,16 @@
bool IPCThreadState::flushIfNeeded()
{
- if (mIsLooper || mServingStackPointer != nullptr) {
+ if (mIsLooper || mServingStackPointer != nullptr || mIsFlushing) {
return false;
}
+ mIsFlushing = true;
// In case this thread is not a looper and is not currently serving a binder transaction,
// there's no guarantee that this thread will call back into the kernel driver any time
// soon. Therefore, flush pending commands such as BC_FREE_BUFFER, to prevent them from getting
// stuck in this thread's out buffer.
flushCommands();
+ mIsFlushing = false;
return true;
}
@@ -846,15 +849,15 @@
}
IPCThreadState::IPCThreadState()
- : mProcess(ProcessState::self()),
- mServingStackPointer(nullptr),
- mWorkSource(kUnsetWorkSource),
- mPropagateWorkSource(false),
- mIsLooper(false),
- mStrictModePolicy(0),
- mLastTransactionBinderFlags(0),
- mCallRestriction(mProcess->mCallRestriction)
-{
+ : mProcess(ProcessState::self()),
+ mServingStackPointer(nullptr),
+ mWorkSource(kUnsetWorkSource),
+ mPropagateWorkSource(false),
+ mIsLooper(false),
+ mIsFlushing(false),
+ mStrictModePolicy(0),
+ mLastTransactionBinderFlags(0),
+ mCallRestriction(mProcess->mCallRestriction) {
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
@@ -894,6 +897,11 @@
}
switch (cmd) {
+ case BR_ONEWAY_SPAM_SUSPECT:
+ ALOGE("Process seems to be sending too many oneway calls.");
+ CallStack::logStack("oneway spamming", CallStack::getCurrent().get(),
+ ANDROID_LOG_ERROR);
+ [[fallthrough]];
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 61f4581..f684cf6 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -75,6 +75,7 @@
sp<IBinder> waitForService(const String16& name16) override;
bool isDeclared(const String16& name) override;
Vector<String16> getDeclaredInstances(const String16& interface) override;
+ std::optional<String16> updatableViaApex(const String16& name) override;
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
@@ -388,4 +389,12 @@
return res;
}
+std::optional<String16> ServiceManagerShim::updatableViaApex(const String16& name) {
+ std::optional<std::string> declared;
+ if (!mTheRealServiceManager->updatableViaApex(String8(name).c_str(), &declared).isOk()) {
+ return std::nullopt;
+ }
+ return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
+}
+
} // namespace android
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index b503beb..6165911 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -123,16 +123,20 @@
std::string regStr = (reRegister) ? "Re-registering" : "Registering";
ALOGI("%s service %s", regStr.c_str(), name.c_str());
- if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
- ALOGE("Failed to register service %s", name.c_str());
+ if (Status status = manager->addService(name.c_str(), service, allowIsolated, dumpFlags);
+ !status.isOk()) {
+ ALOGE("Failed to register service %s (%s)", name.c_str(), status.toString8().c_str());
return false;
}
if (!reRegister) {
- if (!manager->registerClientCallback(name, service,
- sp<android::os::IClientCallback>::fromExisting(this))
- .isOk()) {
- ALOGE("Failed to add client callback for service %s", name.c_str());
+ if (Status status =
+ manager->registerClientCallback(name, service,
+ sp<android::os::IClientCallback>::fromExisting(
+ this));
+ !status.isOk()) {
+ ALOGE("Failed to add client callback for service %s (%s)", name.c_str(),
+ status.toString8().c_str());
return false;
}
@@ -171,10 +175,10 @@
auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
for (auto& [name, entry] : mRegisteredServices) {
- bool success = manager->tryUnregisterService(name, entry.service).isOk();
+ Status status = manager->tryUnregisterService(name, entry.service);
- if (!success) {
- ALOGI("Failed to unregister service %s", name.c_str());
+ if (!status.isOk()) {
+ ALOGI("Failed to unregister service %s (%s)", name.c_str(), status.toString8().c_str());
return false;
}
entry.registered = false;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a735309..ee834ea 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -48,10 +48,10 @@
#include <utils/String8.h>
#include <utils/misc.h>
-#include <private/binder/binder_module.h>
#include "RpcState.h"
#include "Static.h"
#include "Utils.h"
+#include "binder_module.h"
#define LOG_REFS(...)
//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -78,7 +78,11 @@
namespace android {
// many things compile this into prebuilts on the stack
-static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
+#ifdef __LP64__
+static_assert(sizeof(Parcel) == 120);
+#else
+static_assert(sizeof(Parcel) == 60);
+#endif
static std::atomic<size_t> gParcelGlobalAllocCount;
static std::atomic<size_t> gParcelGlobalAllocSize;
@@ -198,7 +202,7 @@
status_t status = writeInt32(1); // non-null
if (status != OK) return status;
RpcAddress address = RpcAddress::zero();
- status = mConnection->state()->onBinderLeaving(mConnection, binder, &address);
+ status = mSession->state()->onBinderLeaving(mSession, binder, &address);
if (status != OK) return status;
status = address.writeToParcel(this);
if (status != OK) return status;
@@ -269,8 +273,7 @@
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
if (isForRpc()) {
- LOG_ALWAYS_FATAL_IF(mConnection == nullptr,
- "RpcConnection required to read from remote parcel");
+ LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
int32_t isNull;
status_t status = readInt32(&isNull);
@@ -282,7 +285,7 @@
auto addr = RpcAddress::zero();
status_t status = addr.readFromParcel(*this);
if (status != OK) return status;
- binder = mConnection->state()->onBinderEntering(mConnection, addr);
+ binder = mSession->state()->onBinderEntering(mSession, addr);
}
return finishUnflattenBinder(binder, out);
@@ -564,20 +567,20 @@
LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
- markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
+ markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession());
}
}
-void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+void Parcel::markForRpc(const sp<RpcSession>& session) {
LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
"format must be set before data is written OR on IPC data");
- LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
- mConnection = connection;
+ LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session");
+ mSession = session;
}
bool Parcel::isForRpc() const {
- return mConnection != nullptr;
+ return mSession != nullptr;
}
void Parcel::updateWorkSourceRequestHeaderPosition() const {
@@ -2495,7 +2498,7 @@
mDataPos = 0;
ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
- mConnection = nullptr;
+ mSession = nullptr;
mObjects = nullptr;
mObjectsSize = 0;
mObjectsCapacity = 0;
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index 6eae5ef..670fd55 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -109,5 +109,10 @@
return granted;
}
+void PermissionCache::purgeCache() {
+ PermissionCache& pc(PermissionCache::getInstance());
+ pc.purge();
+}
+
// ---------------------------------------------------------------------------
} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 7647a8c..4fd0dc7 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -27,8 +27,8 @@
#include <utils/String8.h>
#include <utils/threads.h>
-#include <private/binder/binder_module.h>
#include "Static.h"
+#include "binder_module.h"
#include <errno.h>
#include <fcntl.h>
@@ -43,6 +43,7 @@
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15
+#define DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION 1
#ifdef __ANDROID_VNDK__
const char* kDefaultDriver = "/dev/vndbinder";
@@ -358,6 +359,15 @@
return result;
}
+status_t ProcessState::enableOnewaySpamDetection(bool enable) {
+ uint32_t enableDetection = enable ? 1 : 0;
+ if (ioctl(mDriverFD, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enableDetection) == -1) {
+ ALOGI("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+ return -errno;
+ }
+ return NO_ERROR;
+}
+
void ProcessState::giveThreadPoolName() {
androidSetThreadName( makeBinderThreadName().string() );
}
@@ -388,6 +398,11 @@
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
+ uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
+ result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
+ if (result == -1) {
+ ALOGI("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+ }
} else {
ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
}
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
deleted file mode 100644
index 4aff92b..0000000
--- a/libs/binder/RpcConnection.cpp
+++ /dev/null
@@ -1,368 +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.
- */
-
-#define LOG_TAG "RpcConnection"
-
-#include <binder/RpcConnection.h>
-
-#include <binder/Parcel.h>
-#include <binder/Stability.h>
-#include <utils/String8.h>
-
-#include "RpcState.h"
-#include "RpcWireFormat.h"
-
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#ifdef __GLIBC__
-extern "C" pid_t gettid();
-#endif
-
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif
-
-namespace android {
-
-using base::unique_fd;
-
-RpcConnection::SocketAddress::~SocketAddress() {}
-
-RpcConnection::RpcConnection() {
- LOG_RPC_DETAIL("RpcConnection created %p", this);
-
- mState = std::make_unique<RpcState>();
-}
-RpcConnection::~RpcConnection() {
- LOG_RPC_DETAIL("RpcConnection destroyed %p", this);
-}
-
-sp<RpcConnection> RpcConnection::make() {
- return sp<RpcConnection>::make();
-}
-
-class UnixSocketAddress : public RpcConnection::SocketAddress {
-public:
- explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
- unsigned int pathLen = strlen(path) + 1;
- LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "Socket path is too long: %u %s",
- pathLen, path);
- memcpy(mAddr.sun_path, path, pathLen);
- }
- virtual ~UnixSocketAddress() {}
- std::string toString() const override {
- return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
- mAddr.sun_path)
- .c_str();
- }
- const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
- size_t addrSize() const override { return sizeof(mAddr); }
-
-private:
- sockaddr_un mAddr;
-};
-
-bool RpcConnection::setupUnixDomainServer(const char* path) {
- return setupSocketServer(UnixSocketAddress(path));
-}
-
-bool RpcConnection::addUnixDomainClient(const char* path) {
- return addSocketClient(UnixSocketAddress(path));
-}
-
-#ifdef __BIONIC__
-
-class VsockSocketAddress : public RpcConnection::SocketAddress {
-public:
- VsockSocketAddress(unsigned int cid, unsigned int port)
- : mAddr({
- .svm_family = AF_VSOCK,
- .svm_port = port,
- .svm_cid = cid,
- }) {}
- virtual ~VsockSocketAddress() {}
- std::string toString() const override {
- return String8::format("cid %u port %u", mAddr.svm_cid, mAddr.svm_port).c_str();
- }
- const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
- size_t addrSize() const override { return sizeof(mAddr); }
-
-private:
- sockaddr_vm mAddr;
-};
-
-bool RpcConnection::setupVsockServer(unsigned int port) {
- // realizing value w/ this type at compile time to avoid ubsan abort
- constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
-
- return setupSocketServer(VsockSocketAddress(kAnyCid, port));
-}
-
-bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
- return addSocketClient(VsockSocketAddress(cid, port));
-}
-
-#endif // __BIONIC__
-
-bool RpcConnection::addNullDebuggingClient() {
- unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
-
- if (serverFd == -1) {
- ALOGE("Could not connect to /dev/null: %s", strerror(errno));
- return false;
- }
-
- addClient(std::move(serverFd));
- return true;
-}
-
-sp<IBinder> RpcConnection::getRootObject() {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
- return state()->getRootObject(socket.fd(), sp<RpcConnection>::fromExisting(this));
-}
-
-status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this),
- (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
- : SocketUse::CLIENT);
- return state()->transact(socket.fd(), address, code, data,
- sp<RpcConnection>::fromExisting(this), reply, flags);
-}
-
-status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT_REFCOUNT);
- return state()->sendDecStrong(socket.fd(), address);
-}
-
-void RpcConnection::join() {
- // establish a connection
- {
- unique_fd clientFd(
- TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
- if (clientFd < 0) {
- // If this log becomes confusing, should save more state from setupUnixDomainServer
- // in order to output here.
- ALOGE("Could not accept4 socket: %s", strerror(errno));
- return;
- }
-
- LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
- assignServerToThisThread(std::move(clientFd));
- }
-
- // We may not use the connection we just established (two threads might
- // establish connections for each other), but for now, just use one
- // server/socket connection.
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::SERVER);
-
- while (true) {
- status_t error =
- state()->getAndExecuteCommand(socket.fd(), sp<RpcConnection>::fromExisting(this));
-
- if (error != OK) {
- ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str());
- return;
- }
- }
-}
-
-void RpcConnection::setForServer(const wp<RpcServer>& server) {
- mForServer = server;
-}
-
-wp<RpcServer> RpcConnection::server() {
- return mForServer;
-}
-
-bool RpcConnection::setupSocketServer(const SocketAddress& addr) {
- LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server.");
-
- unique_fd serverFd(
- TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- ALOGE("Could not create socket: %s", strerror(errno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
- int savedErrno = errno;
- ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
- int savedErrno = errno;
- ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- mServer = std::move(serverFd);
- return true;
-}
-
-bool RpcConnection::addSocketClient(const SocketAddress& addr) {
- unique_fd serverFd(
- TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- int savedErrno = errno;
- ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
- int savedErrno = errno;
- ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
-
- addClient(std::move(serverFd));
- return true;
-}
-
-void RpcConnection::addClient(unique_fd&& fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
- connection->fd = std::move(fd);
- mClients.push_back(connection);
-}
-
-void RpcConnection::assignServerToThisThread(unique_fd&& fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
- connection->fd = std::move(fd);
- mServers.push_back(connection);
-}
-
-RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use)
- : mConnection(connection) {
- pid_t tid = gettid();
- std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
-
- mConnection->mWaitingThreads++;
- while (true) {
- sp<ConnectionSocket> exclusive;
- sp<ConnectionSocket> available;
-
- // CHECK FOR DEDICATED CLIENT SOCKET
- //
- // A server/looper should always use a dedicated connection.
- if (use != SocketUse::SERVER) {
- findSocket(tid, &exclusive, &available, mConnection->mClients,
- mConnection->mClientsOffset);
-
- // WARNING: this assumes a server cannot request its client to send
- // a transaction, as mServers is excluded below.
- //
- // Imagine we have more than one thread in play, and a single thread
- // sends a synchronous, then an asynchronous command. Imagine the
- // asynchronous command is sent on the first client socket. Then, if
- // we naively send a synchronous command to that same socket, the
- // thread on the far side might be busy processing the asynchronous
- // command. So, we move to considering the second available thread
- // for subsequent calls.
- if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
- mConnection->mClientsOffset =
- (mConnection->mClientsOffset + 1) % mConnection->mClients.size();
- }
- }
-
- // USE SERVING SOCKET (to start serving or for nested transaction)
- //
- // asynchronous calls cannot be nested
- if (use != SocketUse::CLIENT_ASYNC) {
- // servers should start serving on an available thread only
- // otherwise, this should only be a nested call
- bool useAvailable = use == SocketUse::SERVER;
-
- findSocket(tid, &exclusive, (useAvailable ? &available : nullptr),
- mConnection->mServers, 0 /* index hint */);
- }
-
- // if our thread is already using a connection, prioritize using that
- if (exclusive != nullptr) {
- mSocket = exclusive;
- mReentrant = true;
- break;
- } else if (available != nullptr) {
- mSocket = available;
- mSocket->exclusiveTid = tid;
- break;
- }
-
- LOG_ALWAYS_FATAL_IF(use == SocketUse::SERVER, "Must create connection to join one.");
-
- // in regular binder, this would usually be a deadlock :)
- LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0,
- "Not a client of any connection. You must create a connection to an "
- "RPC server to make any non-nested (e.g. oneway or on another thread) "
- "calls.");
-
- LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...",
- mConnection->mClients.size(), mConnection->mServers.size());
- mConnection->mSocketCv.wait(_l);
- }
- mConnection->mWaitingThreads--;
-}
-
-void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
- sp<ConnectionSocket>* available,
- std::vector<sp<ConnectionSocket>>& sockets,
- size_t socketsIndexHint) {
- LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
- "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
-
- if (*exclusive != nullptr) return; // consistent with break below
-
- for (size_t i = 0; i < sockets.size(); i++) {
- sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
-
- // take first available connection (intuition = caching)
- if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
- *available = socket;
- continue;
- }
-
- // though, prefer to take connection which is already inuse by this thread
- // (nested transactions)
- if (exclusive && socket->exclusiveTid == tid) {
- *exclusive = socket;
- break; // consistent with return above
- }
- }
-}
-
-RpcConnection::ExclusiveSocket::~ExclusiveSocket() {
- // reentrant use of a connection means something less deep in the call stack
- // is using this fd, and it retains the right to it. So, we don't give up
- // exclusive ownership, and no thread is freed.
- if (!mReentrant) {
- std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
- mSocket->exclusiveTid = std::nullopt;
- if (mConnection->mWaitingThreads > 0) {
- _l.unlock();
- mConnection->mSocketCv.notify_one();
- }
- }
-}
-
-} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 1fa37ba..9cc6e7f 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -19,17 +19,21 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include <thread>
#include <vector>
+#include <android-base/scopeguard.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <log/log.h>
#include "RpcState.h"
+#include "RpcSocketAddress.h"
#include "RpcWireFormat.h"
namespace android {
+using base::ScopeGuard;
using base::unique_fd;
RpcServer::RpcServer() {}
@@ -43,22 +47,251 @@
mAgreedExperimental = true;
}
-sp<RpcConnection> RpcServer::addClientConnection() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+bool RpcServer::setupUnixDomainServer(const char* path) {
+ return setupSocketServer(UnixSocketAddress(path));
+}
- auto connection = RpcConnection::make();
- connection->setForServer(sp<RpcServer>::fromExisting(this));
- mConnections.push_back(connection);
- return connection;
+bool RpcServer::setupVsockServer(unsigned int port) {
+ // realizing value w/ this type at compile time to avoid ubsan abort
+ constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
+
+ return setupSocketServer(VsockSocketAddress(kAnyCid, port));
+}
+
+bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) {
+ const char* kAddr = "127.0.0.1";
+
+ if (assignedPort != nullptr) *assignedPort = 0;
+ auto aiStart = InetSocketAddress::getAddrInfo(kAddr, port);
+ if (aiStart == nullptr) return false;
+ for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+ InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, kAddr, port);
+ if (!setupSocketServer(socketAddress)) {
+ continue;
+ }
+
+ LOG_ALWAYS_FATAL_IF(socketAddress.addr()->sa_family != AF_INET, "expecting inet");
+ sockaddr_in addr{};
+ socklen_t len = sizeof(addr);
+ if (0 != getsockname(mServer.get(), reinterpret_cast<sockaddr*>(&addr), &len)) {
+ int savedErrno = errno;
+ ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+ LOG_ALWAYS_FATAL_IF(len != sizeof(addr), "Wrong socket type: len %zu vs len %zu",
+ static_cast<size_t>(len), sizeof(addr));
+ unsigned int realPort = ntohs(addr.sin_port);
+ LOG_ALWAYS_FATAL_IF(port != 0 && realPort != port,
+ "Requesting inet server on %s but it is set up on %u.",
+ socketAddress.toString().c_str(), realPort);
+
+ if (assignedPort != nullptr) {
+ *assignedPort = realPort;
+ }
+
+ return true;
+ }
+ ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", kAddr,
+ port);
+ return false;
+}
+
+void RpcServer::setMaxThreads(size_t threads) {
+ LOG_ALWAYS_FATAL_IF(threads <= 0, "RpcServer is useless without threads");
+ LOG_ALWAYS_FATAL_IF(mStarted, "must be called before started");
+ mMaxThreads = threads;
+}
+
+size_t RpcServer::getMaxThreads() {
+ return mMaxThreads;
}
void RpcServer::setRootObject(const sp<IBinder>& binder) {
- LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object");
- mRootObject = binder;
+ std::lock_guard<std::mutex> _l(mLock);
+ mRootObjectWeak = mRootObject = binder;
+}
+
+void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mRootObject.clear();
+ mRootObjectWeak = binder;
}
sp<IBinder> RpcServer::getRootObject() {
- return mRootObject;
+ std::lock_guard<std::mutex> _l(mLock);
+ bool hasWeak = mRootObjectWeak.unsafe_get();
+ sp<IBinder> ret = mRootObjectWeak.promote();
+ ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr");
+ return ret;
+}
+
+void RpcServer::join() {
+ while (true) {
+ (void)acceptOne();
+ }
+}
+
+bool RpcServer::acceptOne() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ LOG_ALWAYS_FATAL_IF(!hasServer(), "RpcServer must be setup to join.");
+
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
+
+ if (clientFd < 0) {
+ ALOGE("Could not accept4 socket: %s", strerror(errno));
+ return false;
+ }
+ LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ std::thread thread =
+ std::thread(&RpcServer::establishConnection, this,
+ std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd));
+ mConnectingThreads[thread.get_id()] = std::move(thread);
+ }
+
+ return true;
+}
+
+std::vector<sp<RpcSession>> RpcServer::listSessions() {
+ std::lock_guard<std::mutex> _l(mLock);
+ std::vector<sp<RpcSession>> sessions;
+ for (auto& [id, session] : mSessions) {
+ (void)id;
+ sessions.push_back(session);
+ }
+ return sessions;
+}
+
+size_t RpcServer::numUninitializedSessions() {
+ std::lock_guard<std::mutex> _l(mLock);
+ return mConnectingThreads.size();
+}
+
+void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) {
+ LOG_ALWAYS_FATAL_IF(this != server.get(), "Must pass same ownership object");
+
+ // TODO(b/183988761): cannot trust this simple ID
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ bool idValid = true;
+ int32_t id;
+ if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
+ ALOGE("Could not read ID from fd %d", clientFd.get());
+ idValid = false;
+ }
+
+ std::thread thisThread;
+ sp<RpcSession> session;
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+
+ auto threadId = mConnectingThreads.find(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(),
+ "Must establish connection on owned thread");
+ thisThread = std::move(threadId->second);
+ ScopeGuard detachGuard = [&]() { thisThread.detach(); };
+ mConnectingThreads.erase(threadId);
+
+ if (!idValid) {
+ return;
+ }
+
+ if (id == RPC_SESSION_ID_NEW) {
+ LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+ mSessionIdCounter++;
+
+ session = RpcSession::make();
+ session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+
+ mSessions[mSessionIdCounter] = session;
+ } else {
+ auto it = mSessions.find(id);
+ if (it == mSessions.end()) {
+ ALOGE("Cannot add thread, no record of session with ID %d", id);
+ return;
+ }
+ session = it->second;
+ }
+
+ detachGuard.Disable();
+ session->preJoin(std::move(thisThread));
+ }
+
+ // avoid strong cycle
+ server = nullptr;
+ //
+ //
+ // DO NOT ACCESS MEMBER VARIABLES BELOW
+ //
+
+ session->join(std::move(clientFd));
+}
+
+bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
+ LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
+ LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
+
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ ALOGE("Could not create socket: %s", strerror(errno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+ int savedErrno = errno;
+ ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
+
+ mServer = std::move(serverFd);
+ return true;
+}
+
+void RpcServer::onSessionTerminating(const sp<RpcSession>& session) {
+ auto id = session->mId;
+ LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
+ LOG_RPC_DETAIL("Dropping session %d", *id);
+
+ std::lock_guard<std::mutex> _l(mLock);
+ auto it = mSessions.find(*id);
+ LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %d", *id);
+ LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %d", *id);
+ (void)mSessions.erase(it);
+}
+
+bool RpcServer::hasServer() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ return mServer.ok();
+}
+
+unique_fd RpcServer::releaseServer() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ return std::move(mServer);
+}
+
+bool RpcServer::setupExternalServer(base::unique_fd serverFd) {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ if (mServer.ok()) {
+ ALOGE("Each RpcServer can only have one server.");
+ return false;
+ }
+ mServer = std::move(serverFd);
+ return true;
}
} // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
new file mode 100644
index 0000000..05fa49e
--- /dev/null
+++ b/libs/binder/RpcSession.cpp
@@ -0,0 +1,407 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "RpcSession"
+
+#include <binder/RpcSession.h>
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string_view>
+
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/Stability.h>
+#include <utils/String8.h>
+
+#include "RpcSocketAddress.h"
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#ifdef __GLIBC__
+extern "C" pid_t gettid();
+#endif
+
+namespace android {
+
+using base::unique_fd;
+
+RpcSession::RpcSession() {
+ LOG_RPC_DETAIL("RpcSession created %p", this);
+
+ mState = std::make_unique<RpcState>();
+}
+RpcSession::~RpcSession() {
+ LOG_RPC_DETAIL("RpcSession destroyed %p", this);
+
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mServerConnections.size() != 0,
+ "Should not be able to destroy a session with servers in use.");
+}
+
+sp<RpcSession> RpcSession::make() {
+ return sp<RpcSession>::make();
+}
+
+bool RpcSession::setupUnixDomainClient(const char* path) {
+ return setupSocketClient(UnixSocketAddress(path));
+}
+
+bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
+ return setupSocketClient(VsockSocketAddress(cid, port));
+}
+
+bool RpcSession::setupInetClient(const char* addr, unsigned int port) {
+ auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
+ if (aiStart == nullptr) return false;
+ for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+ InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
+ if (setupSocketClient(socketAddress)) return true;
+ }
+ ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
+ return false;
+}
+
+bool RpcSession::addNullDebuggingClient() {
+ unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
+
+ if (serverFd == -1) {
+ ALOGE("Could not connect to /dev/null: %s", strerror(errno));
+ return false;
+ }
+
+ addClientConnection(std::move(serverFd));
+ return true;
+}
+
+sp<IBinder> RpcSession::getRootObject() {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ return state()->getRootObject(connection.fd(), sp<RpcSession>::fromExisting(this));
+}
+
+status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ return state()->getMaxThreads(connection.fd(), sp<RpcSession>::fromExisting(this), maxThreads);
+}
+
+status_t RpcSession::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+ (flags & IBinder::FLAG_ONEWAY) ? ConnectionUse::CLIENT_ASYNC
+ : ConnectionUse::CLIENT);
+ return state()->transact(connection.fd(), address, code, data,
+ sp<RpcSession>::fromExisting(this), reply, flags);
+}
+
+status_t RpcSession::sendDecStrong(const RpcAddress& address) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+ ConnectionUse::CLIENT_REFCOUNT);
+ return state()->sendDecStrong(connection.fd(), address);
+}
+
+status_t RpcSession::readId() {
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
+ }
+
+ int32_t id;
+
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ status_t status =
+ state()->getSessionId(connection.fd(), sp<RpcSession>::fromExisting(this), &id);
+ if (status != OK) return status;
+
+ LOG_RPC_DETAIL("RpcSession %p has id %d", this, id);
+ mId = id;
+ return OK;
+}
+
+void RpcSession::preJoin(std::thread thread) {
+ LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread");
+
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ mThreads[thread.get_id()] = std::move(thread);
+ }
+}
+
+void RpcSession::join(unique_fd client) {
+ // must be registered to allow arbitrary client code executing commands to
+ // be able to do nested calls (we can't only read from it)
+ sp<RpcConnection> connection = assignServerToThisThread(std::move(client));
+
+ while (true) {
+ status_t error =
+ state()->getAndExecuteCommand(connection->fd, sp<RpcSession>::fromExisting(this));
+
+ if (error != OK) {
+ ALOGI("Binder connection thread closing w/ status %s", statusToString(error).c_str());
+ break;
+ }
+ }
+
+ LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection),
+ "bad state: connection object guaranteed to be in list");
+
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ auto it = mThreads.find(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(it == mThreads.end());
+ it->second.detach();
+ mThreads.erase(it);
+ }
+}
+
+void RpcSession::terminateLocked() {
+ // TODO(b/185167543):
+ // - kindly notify other side of the connection of termination (can't be
+ // locked)
+ // - prevent new client/servers from being added
+ // - stop all threads which are currently reading/writing
+ // - terminate RpcState?
+
+ if (mTerminated) return;
+
+ sp<RpcServer> server = mForServer.promote();
+ if (server) {
+ server->onSessionTerminating(sp<RpcSession>::fromExisting(this));
+ }
+}
+
+wp<RpcServer> RpcSession::server() {
+ return mForServer;
+}
+
+bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mClientConnections.size() != 0,
+ "Must only setup session once, but already has %zu clients",
+ mClientConnections.size());
+ }
+
+ if (!setupOneSocketClient(addr, RPC_SESSION_ID_NEW)) return false;
+
+ // TODO(b/185167543): we should add additional sessions dynamically
+ // instead of all at once.
+ // TODO(b/186470974): first risk of blocking
+ size_t numThreadsAvailable;
+ if (status_t status = getRemoteMaxThreads(&numThreadsAvailable); status != OK) {
+ ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(),
+ statusToString(status).c_str());
+ return false;
+ }
+
+ if (status_t status = readId(); status != OK) {
+ ALOGE("Could not get session id after initial session to %s; %s", addr.toString().c_str(),
+ statusToString(status).c_str());
+ return false;
+ }
+
+ // we've already setup one client
+ for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+ // TODO(b/185167543): shutdown existing connections?
+ if (!setupOneSocketClient(addr, mId.value())) return false;
+ }
+
+ return true;
+}
+
+bool RpcSession::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) {
+ for (size_t tries = 0; tries < 5; tries++) {
+ if (tries > 0) usleep(10000);
+
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not create socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ if (errno == ECONNRESET) {
+ ALOGW("Connection reset on %s", addr.toString().c_str());
+ continue;
+ }
+ int savedErrno = errno;
+ ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
+ if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
+ int savedErrno = errno;
+ ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+ addClientConnection(std::move(serverFd));
+ return true;
+ }
+
+ ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
+ return false;
+}
+
+void RpcSession::addClientConnection(unique_fd fd) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ sp<RpcConnection> session = sp<RpcConnection>::make();
+ session->fd = std::move(fd);
+ mClientConnections.push_back(session);
+}
+
+void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId) {
+ mId = sessionId;
+ mForServer = server;
+}
+
+sp<RpcSession::RpcConnection> RpcSession::assignServerToThisThread(unique_fd fd) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ sp<RpcConnection> session = sp<RpcConnection>::make();
+ session->fd = std::move(fd);
+ session->exclusiveTid = gettid();
+ mServerConnections.push_back(session);
+
+ return session;
+}
+
+bool RpcSession::removeServerConnection(const sp<RpcConnection>& connection) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ if (auto it = std::find(mServerConnections.begin(), mServerConnections.end(), connection);
+ it != mServerConnections.end()) {
+ mServerConnections.erase(it);
+ if (mServerConnections.size() == 0) {
+ terminateLocked();
+ }
+ return true;
+ }
+ return false;
+}
+
+RpcSession::ExclusiveConnection::ExclusiveConnection(const sp<RpcSession>& session,
+ ConnectionUse use)
+ : mSession(session) {
+ pid_t tid = gettid();
+ std::unique_lock<std::mutex> _l(mSession->mMutex);
+
+ mSession->mWaitingThreads++;
+ while (true) {
+ sp<RpcConnection> exclusive;
+ sp<RpcConnection> available;
+
+ // CHECK FOR DEDICATED CLIENT SOCKET
+ //
+ // A server/looper should always use a dedicated session if available
+ findConnection(tid, &exclusive, &available, mSession->mClientConnections,
+ mSession->mClientConnectionsOffset);
+
+ // WARNING: this assumes a server cannot request its client to send
+ // a transaction, as mServerConnections is excluded below.
+ //
+ // Imagine we have more than one thread in play, and a single thread
+ // sends a synchronous, then an asynchronous command. Imagine the
+ // asynchronous command is sent on the first client connection. Then, if
+ // we naively send a synchronous command to that same connection, the
+ // thread on the far side might be busy processing the asynchronous
+ // command. So, we move to considering the second available thread
+ // for subsequent calls.
+ if (use == ConnectionUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
+ mSession->mClientConnectionsOffset =
+ (mSession->mClientConnectionsOffset + 1) % mSession->mClientConnections.size();
+ }
+
+ // USE SERVING SOCKET (for nested transaction)
+ //
+ // asynchronous calls cannot be nested
+ if (use != ConnectionUse::CLIENT_ASYNC) {
+ // server connections are always assigned to a thread
+ findConnection(tid, &exclusive, nullptr /*available*/, mSession->mServerConnections,
+ 0 /* index hint */);
+ }
+
+ // if our thread is already using a session, prioritize using that
+ if (exclusive != nullptr) {
+ mConnection = exclusive;
+ mReentrant = true;
+ break;
+ } else if (available != nullptr) {
+ mConnection = available;
+ mConnection->exclusiveTid = tid;
+ break;
+ }
+
+ // in regular binder, this would usually be a deadlock :)
+ LOG_ALWAYS_FATAL_IF(mSession->mClientConnections.size() == 0,
+ "Not a client of any session. You must create a session to an "
+ "RPC server to make any non-nested (e.g. oneway or on another thread) "
+ "calls.");
+
+ LOG_RPC_DETAIL("No available session (have %zu clients and %zu servers). Waiting...",
+ mSession->mClientConnections.size(), mSession->mServerConnections.size());
+ mSession->mAvailableConnectionCv.wait(_l);
+ }
+ mSession->mWaitingThreads--;
+}
+
+void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ sp<RpcConnection>* available,
+ std::vector<sp<RpcConnection>>& sockets,
+ size_t socketsIndexHint) {
+ LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
+ "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
+
+ if (*exclusive != nullptr) return; // consistent with break below
+
+ for (size_t i = 0; i < sockets.size(); i++) {
+ sp<RpcConnection>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
+
+ // take first available session (intuition = caching)
+ if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
+ *available = socket;
+ continue;
+ }
+
+ // though, prefer to take session which is already inuse by this thread
+ // (nested transactions)
+ if (exclusive && socket->exclusiveTid == tid) {
+ *exclusive = socket;
+ break; // consistent with return above
+ }
+ }
+}
+
+RpcSession::ExclusiveConnection::~ExclusiveConnection() {
+ // reentrant use of a session means something less deep in the call stack
+ // is using this fd, and it retains the right to it. So, we don't give up
+ // exclusive ownership, and no thread is freed.
+ if (!mReentrant) {
+ std::unique_lock<std::mutex> _l(mSession->mMutex);
+ mConnection->exclusiveTid = std::nullopt;
+ if (mSession->mWaitingThreads > 0) {
+ _l.unlock();
+ mSession->mAvailableConnectionCv.notify_one();
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h
new file mode 100644
index 0000000..c7ba5d9
--- /dev/null
+++ b/libs/binder/RpcSocketAddress.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include "vm_sockets.h"
+
+namespace android {
+
+class RpcSocketAddress {
+public:
+ virtual ~RpcSocketAddress() {}
+ virtual std::string toString() const = 0;
+ virtual const sockaddr* addr() const = 0;
+ virtual size_t addrSize() const = 0;
+};
+
+class UnixSocketAddress : public RpcSocketAddress {
+public:
+ explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
+ unsigned int pathLen = strlen(path) + 1;
+ LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "Socket path is too long: %u %s",
+ pathLen, path);
+ memcpy(mAddr.sun_path, path, pathLen);
+ }
+ virtual ~UnixSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
+ mAddr.sun_path)
+ .c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_un mAddr;
+};
+
+class VsockSocketAddress : public RpcSocketAddress {
+public:
+ VsockSocketAddress(unsigned int cid, unsigned int port)
+ : mAddr({
+ .svm_family = AF_VSOCK,
+ .svm_port = port,
+ .svm_cid = cid,
+ }) {}
+ virtual ~VsockSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("cid %u port %u", mAddr.svm_cid, mAddr.svm_port).c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_vm mAddr;
+};
+
+class InetSocketAddress : public RpcSocketAddress {
+public:
+ InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port)
+ : mSockAddr(sockAddr), mSize(size), mAddr(addr), mPort(port) {}
+ [[nodiscard]] std::string toString() const override {
+ return String8::format("%s:%u", mAddr, mPort).c_str();
+ }
+ [[nodiscard]] const sockaddr* addr() const override { return mSockAddr; }
+ [[nodiscard]] size_t addrSize() const override { return mSize; }
+
+ using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>;
+ static AddrInfo getAddrInfo(const char* addr, unsigned int port) {
+ addrinfo hint{
+ .ai_flags = 0,
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_protocol = 0,
+ };
+ addrinfo* aiStart = nullptr;
+ if (int rc = getaddrinfo(addr, std::to_string(port).data(), &hint, &aiStart); 0 != rc) {
+ ALOGE("Unable to resolve %s:%u: %s", addr, port, gai_strerror(rc));
+ return AddrInfo(nullptr, nullptr);
+ }
+ if (aiStart == nullptr) {
+ ALOGE("Unable to resolve %s:%u: getaddrinfo returns null", addr, port);
+ return AddrInfo(nullptr, nullptr);
+ }
+ return AddrInfo(aiStart, &freeaddrinfo);
+ }
+
+private:
+ const sockaddr* mSockAddr;
+ size_t mSize;
+ const char* mAddr;
+ unsigned int mPort;
+};
+
+} // namespace android
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index d934136..2ba9fa2 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -31,16 +31,16 @@
RpcState::RpcState() {}
RpcState::~RpcState() {}
-status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder,
+status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
RpcAddress* outAddress) {
bool isRemote = binder->remoteBinder();
bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
- if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) {
+ if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcSession() != session) {
// We need to be able to send instructions over the socket for how to
// connect to a different server, and we also need to let the host
// process know that this is happening.
- ALOGE("Cannot send binder from unrelated binder RPC connection.");
+ ALOGE("Cannot send binder from unrelated binder RPC session.");
return INVALID_OPERATION;
}
@@ -91,8 +91,7 @@
return OK;
}
-sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
- const RpcAddress& address) {
+sp<IBinder> RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address) {
std::unique_lock<std::mutex> _l(mNodeMutex);
if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
@@ -106,7 +105,7 @@
// We have timesRecd RPC refcounts, but we only need to hold on to one
// when we keep the object. All additional dec strongs are sent
// immediately, we wait to send the last one in BpBinder::onLastDecStrong.
- (void)connection->sendDecStrong(address);
+ (void)session->sendDecStrong(address);
return binder;
}
@@ -114,9 +113,9 @@
auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}});
LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy");
- // Currently, all binders are assumed to be part of the same connection (no
+ // Currently, all binders are assumed to be part of the same session (no
// device global binders in the RPC world).
- sp<IBinder> binder = BpBinder::create(connection, it->first);
+ sp<IBinder> binder = BpBinder::create(session, it->first);
it->second.binder = binder;
it->second.timesRecd = 1;
return binder;
@@ -183,6 +182,27 @@
}
}
+RpcState::CommandData::CommandData(size_t size) : mSize(size) {
+ // The maximum size for regular binder is 1MB for all concurrent
+ // transactions. A very small proportion of transactions are even
+ // larger than a page, but we need to avoid allocating too much
+ // data on behalf of an arbitrary client, or we could risk being in
+ // a position where a single additional allocation could run out of
+ // memory.
+ //
+ // Note, this limit may not reflect the total amount of data allocated for a
+ // transaction (in some cases, additional fixed size amounts are added),
+ // though for rough consistency, we should avoid cases where this data type
+ // is used for multiple dynamic allocations for a single transaction.
+ constexpr size_t kMaxTransactionAllocation = 100 * 1000;
+ if (size == 0) return;
+ if (size > kMaxTransactionAllocation) {
+ ALOGW("Transaction requested too much data allocation %zu", size);
+ return;
+ }
+ mData.reset(new (std::nothrow) uint8_t[size]);
+}
+
bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
@@ -232,14 +252,13 @@
return true;
}
-sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd,
- const sp<RpcConnection>& connection) {
+sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session) {
Parcel data;
- data.markForRpc(connection);
+ data.markForRpc(session);
Parcel reply;
- status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data,
- connection, &reply, 0);
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, session,
+ &reply, 0);
if (status != OK) {
ALOGE("Error getting root object: %s", statusToString(status).c_str());
return nullptr;
@@ -248,8 +267,54 @@
return reply.readStrongBinder();
}
+status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
+ size_t* maxThreadsOut) {
+ Parcel data;
+ data.markForRpc(session);
+ Parcel reply;
+
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
+ session, &reply, 0);
+ if (status != OK) {
+ ALOGE("Error getting max threads: %s", statusToString(status).c_str());
+ return status;
+ }
+
+ int32_t maxThreads;
+ status = reply.readInt32(&maxThreads);
+ if (status != OK) return status;
+ if (maxThreads <= 0) {
+ ALOGE("Error invalid max maxThreads: %d", maxThreads);
+ return BAD_VALUE;
+ }
+
+ *maxThreadsOut = maxThreads;
+ return OK;
+}
+
+status_t RpcState::getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+ int32_t* sessionIdOut) {
+ Parcel data;
+ data.markForRpc(session);
+ Parcel reply;
+
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data,
+ session, &reply, 0);
+ if (status != OK) {
+ ALOGE("Error getting session ID: %s", statusToString(status).c_str());
+ return status;
+ }
+
+ int32_t sessionId;
+ status = reply.readInt32(&sessionId);
+ if (status != OK) return status;
+
+ *sessionIdOut = sessionId;
+ return OK;
+}
+
status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
- const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply,
+ const Parcel& data, const sp<RpcSession>& session, Parcel* reply,
uint32_t flags) {
uint64_t asyncNumber = 0;
@@ -282,7 +347,11 @@
.asyncNumber = asyncNumber,
};
- std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+ CommandData transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+ if (!transactionData.valid()) {
+ return NO_MEMORY;
+ }
+
memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction));
memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize());
@@ -309,7 +378,7 @@
LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
- return waitForReply(fd, connection, reply);
+ return waitForReply(fd, session, reply);
}
static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -321,7 +390,7 @@
LOG_ALWAYS_FATAL_IF(objectsCount, 0);
}
-status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
Parcel* reply) {
RpcWireHeader command;
while (true) {
@@ -331,13 +400,16 @@
if (command.command == RPC_COMMAND_REPLY) break;
- status_t status = processServerCommand(fd, connection, command);
+ status_t status = processServerCommand(fd, session, command);
if (status != OK) return status;
}
- uint8_t* data = new uint8_t[command.bodySize];
+ CommandData data(command.bodySize);
+ if (!data.valid()) {
+ return NO_MEMORY;
+ }
- if (!rpcRec(fd, "reply body", data, command.bodySize)) {
+ if (!rpcRec(fd, "reply body", data.data(), command.bodySize)) {
return DEAD_OBJECT;
}
@@ -347,13 +419,14 @@
terminate();
return BAD_VALUE;
}
- RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data);
+ RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data());
if (rpcReply->status != OK) return rpcReply->status;
+ data.release();
reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
nullptr, 0, cleanup_reply_data);
- reply->markForRpc(connection);
+ reply->markForRpc(session);
return OK;
}
@@ -384,8 +457,7 @@
return OK;
}
-status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection) {
+status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, const sp<RpcSession>& session) {
LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get());
RpcWireHeader command;
@@ -393,15 +465,14 @@
return DEAD_OBJECT;
}
- return processServerCommand(fd, connection, command);
+ return processServerCommand(fd, session, command);
}
-status_t RpcState::processServerCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
switch (command.command) {
case RPC_COMMAND_TRANSACT:
- return processTransact(fd, connection, command);
+ return processTransact(fd, session, command);
case RPC_COMMAND_DEC_STRONG:
return processDecStrong(fd, command);
}
@@ -410,21 +481,24 @@
// RPC-binder-level wire protocol is not self synchronizing, we have no way
// to understand where the current command ends and the next one begins. We
// also can't consider it a fatal error because this would allow any client
- // to kill us, so ending the connection for misbehaving client.
- ALOGE("Unknown RPC command %d - terminating connection", command.command);
+ // to kill us, so ending the session for misbehaving client.
+ ALOGE("Unknown RPC command %d - terminating session", command.command);
terminate();
return DEAD_OBJECT;
}
-status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
- std::vector<uint8_t> transactionData(command.bodySize);
+ CommandData transactionData(command.bodySize);
+ if (!transactionData.valid()) {
+ return NO_MEMORY;
+ }
if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
return DEAD_OBJECT;
}
- return processTransactInternal(fd, connection, std::move(transactionData));
+ return processTransactInternal(fd, session, std::move(transactionData));
}
static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -436,9 +510,8 @@
(void)objectsCount;
}
-status_t RpcState::processTransactInternal(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
- std::vector<uint8_t>&& transactionData) {
+status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
+ CommandData transactionData) {
if (transactionData.size() < sizeof(RpcWireTransaction)) {
ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
sizeof(RpcWireTransaction), transactionData.size());
@@ -459,7 +532,6 @@
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
ALOGE("Unknown binder address %s.", addr.toString().c_str());
- dump();
replyStatus = BAD_VALUE;
} else {
target = it->second.binder.promote();
@@ -469,7 +541,7 @@
// However, for local binders, it indicates a misbehaving client
// (any binder which is being transacted on should be holding a
// strong ref count), so in either case, terminating the
- // connection.
+ // session.
ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
addr.toString().c_str());
terminate();
@@ -499,7 +571,7 @@
}
Parcel reply;
- reply.markForRpc(connection);
+ reply.markForRpc(session);
if (replyStatus == OK) {
Parcel data;
@@ -510,29 +582,41 @@
transactionData.size() - offsetof(RpcWireTransaction, data),
nullptr /*object*/, 0 /*objectCount*/,
do_nothing_to_transact_data);
- data.markForRpc(connection);
+ data.markForRpc(session);
if (target) {
replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
} else {
LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
- // special case for 'zero' address (special server commands)
- switch (transaction->code) {
- case RPC_SPECIAL_TRANSACT_GET_ROOT: {
- sp<IBinder> root;
- sp<RpcServer> server = connection->server().promote();
- if (server) {
- root = server->getRootObject();
- } else {
- ALOGE("Root object requested, but no server attached.");
- }
- replyStatus = reply.writeStrongBinder(root);
- break;
+ sp<RpcServer> server = session->server().promote();
+ if (server) {
+ // special case for 'zero' address (special server commands)
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+ replyStatus = reply.writeStrongBinder(server->getRootObject());
+ break;
+ }
+ case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
+ replyStatus = reply.writeInt32(server->getMaxThreads());
+ break;
+ }
+ case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
+ // only sessions w/ services can be the source of a
+ // session ID (so still guarded by non-null server)
+ //
+ // sessions associated with servers must have an ID
+ // (hence abort)
+ int32_t id = session->getPrivateAccessorForId().get().value();
+ replyStatus = reply.writeInt32(id);
+ break;
+ }
+ default: {
+ replyStatus = UNKNOWN_TRANSACTION;
+ }
}
- default: {
- replyStatus = UNKNOWN_TRANSACTION;
- }
+ } else {
+ ALOGE("Special command sent, but no server object attached.");
}
}
}
@@ -577,11 +661,11 @@
// justification for const_cast (consider avoiding priority_queue):
// - AsyncTodo operator< doesn't depend on 'data' object
// - gotta go fast
- std::vector<uint8_t> data = std::move(
+ CommandData data = std::move(
const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
it->second.asyncTodo.pop();
_l.unlock();
- return processTransactInternal(fd, connection, std::move(data));
+ return processTransactInternal(fd, session, std::move(data));
}
}
return OK;
@@ -591,7 +675,10 @@
.status = replyStatus,
};
- std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize());
+ CommandData replyData(sizeof(RpcWireReply) + reply.dataSize());
+ if (!replyData.valid()) {
+ return NO_MEMORY;
+ }
memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply));
memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize());
@@ -618,7 +705,10 @@
status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
- std::vector<uint8_t> commandData(command.bodySize);
+ CommandData commandData(command.bodySize);
+ if (!commandData.valid()) {
+ return NO_MEMORY;
+ }
if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
return DEAD_OBJECT;
}
@@ -637,7 +727,6 @@
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
- dump();
return OK;
}
@@ -670,7 +759,7 @@
}
_l.unlock();
- tempHold = nullptr; // destructor may make binder calls on this connection
+ tempHold = nullptr; // destructor may make binder calls on this session
return OK;
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index f4f5151..31f8a22 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -18,9 +18,10 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/Parcel.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <map>
+#include <optional>
#include <queue>
namespace android {
@@ -43,74 +44,91 @@
/**
* Abstracts away management of ref counts and the wire format from
- * RpcConnection
+ * RpcSession
*/
class RpcState {
public:
RpcState();
~RpcState();
- sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection);
+ // TODO(b/182940634): combine some special transactions into one "getServerInfo" call?
+ sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session);
+ status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
+ size_t* maxThreadsOut);
+ status_t getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+ int32_t* sessionIdOut);
[[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
uint32_t code, const Parcel& data,
- const sp<RpcConnection>& connection, Parcel* reply,
- uint32_t flags);
+ const sp<RpcSession>& session, Parcel* reply, uint32_t flags);
[[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address);
[[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection);
+ const sp<RpcSession>& session);
/**
* Called by Parcel for outgoing binders. This implies one refcount of
* ownership to the outgoing binder.
*/
- [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection,
- const sp<IBinder>& binder, RpcAddress* outAddress);
+ [[nodiscard]] status_t onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
+ RpcAddress* outAddress);
/**
* Called by Parcel for incoming binders. This either returns the refcount
* to the process, if this process already has one, or it takes ownership of
* that refcount
*/
- sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address);
+ sp<IBinder> onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address);
size_t countBinders();
void dump();
private:
/**
- * Called when reading or writing data to a connection fails to clean up
- * data associated with the connection in order to cleanup binders.
+ * Called when reading or writing data to a session fails to clean up
+ * data associated with the session in order to cleanup binders.
* Specifically, we have a strong dependency cycle, since BpBinder is
* OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true).
*
- * BpBinder -> RpcConnection -> RpcState
+ * BpBinder -> RpcSession -> RpcState
* ^-----------------------------/
*
* In the success case, eventually all refcounts should be propagated over
- * the connection, though this could also be called to eagerly cleanup
- * the connection.
+ * the session, though this could also be called to eagerly cleanup
+ * the session.
*
- * WARNING: RpcState is responsible for calling this when the connection is
+ * WARNING: RpcState is responsible for calling this when the session is
* no longer recoverable.
*/
void terminate();
+ // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
+ // large allocations to avoid being requested from allocating too much data.
+ struct CommandData {
+ explicit CommandData(size_t size);
+ bool valid() { return mSize == 0 || mData != nullptr; }
+ size_t size() { return mSize; }
+ uint8_t* data() { return mData.get(); }
+ uint8_t* release() { return mData.release(); }
+
+ private:
+ std::unique_ptr<uint8_t[]> mData;
+ size_t mSize;
+ };
+
[[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
size_t size);
[[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
- [[nodiscard]] status_t waitForReply(const base::unique_fd& fd,
- const sp<RpcConnection>& connection, Parcel* reply);
+ [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
+ Parcel* reply);
[[nodiscard]] status_t processServerCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+ const sp<RpcSession>& session,
const RpcWireHeader& command);
- [[nodiscard]] status_t processTransact(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+ [[nodiscard]] status_t processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command);
[[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
- std::vector<uint8_t>&& transactionData);
+ const sp<RpcSession>& session,
+ CommandData transactionData);
[[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
const RpcWireHeader& command);
@@ -145,7 +163,7 @@
// async transaction queue, _only_ for local binder
struct AsyncTodo {
- std::vector<uint8_t> data; // most convenient format, to move it here
+ CommandData data;
uint64_t asyncNumber = 0;
bool operator<(const AsyncTodo& o) const {
@@ -163,7 +181,7 @@
std::mutex mNodeMutex;
bool mTerminated = false;
- // binders known by both sides of a connection
+ // binders known by both sides of a session
std::map<RpcAddress, BinderNode> mNodeForAddress;
};
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 60ec6c9..c5fa008 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -47,8 +47,12 @@
*/
enum : uint32_t {
RPC_SPECIAL_TRANSACT_GET_ROOT = 0,
+ RPC_SPECIAL_TRANSACT_GET_MAX_THREADS = 1,
+ RPC_SPECIAL_TRANSACT_GET_SESSION_ID = 2,
};
+constexpr int32_t RPC_SESSION_ID_NEW = -1;
+
// serialization is like:
// |RpcWireHeader|struct desginated by 'command'| (over and over again)
diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS
index b99ca09..3100518 100644
--- a/libs/binder/aidl/android/content/pm/OWNERS
+++ b/libs/binder/aidl/android/content/pm/OWNERS
@@ -1,4 +1,5 @@
narayan@google.com
patb@google.com
svetoslavganov@google.com
-toddke@google.com
\ No newline at end of file
+toddke@google.com
+patb@google.com
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 2fabf94..75c4092 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -108,6 +108,11 @@
@utf8InCpp String[] getDeclaredInstances(@utf8InCpp String iface);
/**
+ * If updatable-via-apex, returns the APEX via which this is updated.
+ */
+ @nullable @utf8InCpp String updatableViaApex(@utf8InCpp String name);
+
+ /**
* Request a callback when the number of clients of the service changes.
* Used by LazyServiceRegistrar to dynamically stop services that have no clients.
*/
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/binder_module.h
similarity index 72%
rename from libs/binder/include/private/binder/binder_module.h
rename to libs/binder/binder_module.h
index 1579199..9dea3b4 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -29,18 +29,14 @@
#undef B_PACK_CHARS
#endif
-#include <sys/ioctl.h>
#include <linux/android/binder.h>
-
-#ifdef __cplusplus
-namespace android {
-#endif
+#include <sys/ioctl.h>
#ifndef BR_FROZEN_REPLY
// Temporary definition of BR_FROZEN_REPLY. For production
// this will come from UAPI binder.h
#define BR_FROZEN_REPLY _IO('r', 18)
-#endif //BR_FROZEN_REPLY
+#endif // BR_FROZEN_REPLY
#ifndef BINDER_FREEZE
/*
@@ -53,43 +49,53 @@
//
// Group-leader PID of process to be frozen
//
- uint32_t pid;
+ uint32_t pid;
//
// Enable(1) / Disable(0) freeze for given PID
//
- uint32_t enable;
+ uint32_t enable;
//
// Timeout to wait for transactions to drain.
// 0: don't wait (ioctl will return EAGAIN if not drained)
// N: number of ms to wait
- uint32_t timeout_ms;
+ uint32_t timeout_ms;
};
-#endif //BINDER_FREEZE
+#endif // BINDER_FREEZE
#ifndef BINDER_GET_FROZEN_INFO
-#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
+#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
struct binder_frozen_status_info {
//
// Group-leader PID of process to be queried
//
- __u32 pid;
+ __u32 pid;
//
// Indicates whether the process has received any sync calls since last
// freeze (cleared at freeze/unfreeze)
//
- __u32 sync_recv;
+ __u32 sync_recv;
//
// Indicates whether the process has received any async calls since last
// freeze (cleared at freeze/unfreeze)
//
- __u32 async_recv;
+ __u32 async_recv;
};
-#endif //BINDER_GET_FROZEN_INFO
+#endif // BINDER_GET_FROZEN_INFO
-#ifdef __cplusplus
-} // namespace android
-#endif
+#ifndef BR_ONEWAY_SPAM_SUSPECT
+// Temporary definition of BR_ONEWAY_SPAM_SUSPECT. For production
+// this will come from UAPI binder.h
+#define BR_ONEWAY_SPAM_SUSPECT _IO('r', 19)
+#endif // BR_ONEWAY_SPAM_SUSPECT
+
+#ifndef BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+/*
+ * Temporary definitions for oneway spam detection support. For the final version
+ * these will be defined in the UAPI binder.h file from upstream kernel.
+ */
+#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
+#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index ad618f9..61bf018 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -28,7 +28,7 @@
// ---------------------------------------------------------------------------
namespace android {
-class RpcConnection;
+class RpcSession;
class RpcState;
namespace internal {
class Stability;
@@ -41,11 +41,11 @@
{
public:
static sp<BpBinder> create(int32_t handle);
- static sp<BpBinder> create(const sp<RpcConnection>& connection, const RpcAddress& address);
+ static sp<BpBinder> create(const sp<RpcSession>& session, const RpcAddress& address);
/**
* Return value:
- * true - this is associated with a socket RpcConnection
+ * true - this is associated with a socket RpcSession
* false - (usual) binder over e.g. /dev/binder
*/
bool isRpcBinder() const;
@@ -133,7 +133,7 @@
// valid if isRpcBinder
const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
- const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); }
+ const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
const BpBinder* mBinder;
};
@@ -148,19 +148,19 @@
struct BinderHandle {
int32_t handle;
};
- struct SocketHandle {
- sp<RpcConnection> connection;
+ struct RpcHandle {
+ sp<RpcSession> session;
RpcAddress address;
};
- using Handle = std::variant<BinderHandle, SocketHandle>;
+ using Handle = std::variant<BinderHandle, RpcHandle>;
int32_t binderHandle() const;
const RpcAddress& rpcAddress() const;
- const sp<RpcConnection>& rpcConnection() const;
+ const sp<RpcSession>& rpcSession() const;
explicit BpBinder(Handle&& handle);
BpBinder(BinderHandle&& handle, int32_t trackedUid);
- explicit BpBinder(SocketHandle&& handle);
+ explicit BpBinder(RpcHandle&& handle);
virtual ~BpBinder();
virtual void onFirstRef();
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 31f63c8..97c826c 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/unique_fd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
@@ -49,39 +50,39 @@
{
public:
enum {
- FIRST_CALL_TRANSACTION = 0x00000001,
- LAST_CALL_TRANSACTION = 0x00ffffff,
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x00ffffff,
- PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
- DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'),
- SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
- INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
- SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
- EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
- DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
+ PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'),
+ DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'),
+ SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'),
+ INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
+ SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
+ EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
+ DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
// See android.os.IBinder.TWEET_TRANSACTION
// Most importantly, messages can be anything not exceeding 130 UTF-8
// characters, and callees should exclaim "jolly good message old boy!"
- TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'),
+ TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'),
// See android.os.IBinder.LIKE_TRANSACTION
// Improve binder self-esteem.
- LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'),
+ LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'),
// Corresponds to TF_ONE_WAY -- an asynchronous call.
- FLAG_ONEWAY = 0x00000001,
+ FLAG_ONEWAY = 0x00000001,
// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
// is made
- FLAG_CLEAR_BUF = 0x00000020,
+ FLAG_CLEAR_BUF = 0x00000020,
// Private userspace flag for transaction which is being requested from
// a vendor context.
- FLAG_PRIVATE_VENDOR = 0x10000000,
+ FLAG_PRIVATE_VENDOR = 0x10000000,
};
- IBinder();
+ IBinder();
/**
* Check if this IBinder implements the interface named by
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 23a0cb0..5d04ebe 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -212,6 +212,7 @@
// Whether the work source should be propagated.
bool mPropagateWorkSource;
bool mIsLooper;
+ bool mIsFlushing;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
CallRestriction mCallRestriction;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 5f0d056..3dbe2c4 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -20,6 +20,8 @@
#include <utils/Vector.h>
#include <utils/String16.h>
+#include <optional>
+
namespace android {
// ----------------------------------------------------------------------
@@ -99,6 +101,12 @@
* Get all instances of a service as declared in the VINTF manifest
*/
virtual Vector<String16> getDeclaredInstances(const String16& interface) = 0;
+
+ /**
+ * If this instance is updatable via an APEX, returns the APEX with which
+ * this can be updated.
+ */
+ virtual std::optional<String16> updatableViaApex(const String16& name) = 0;
};
sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 9659732..f3ba830 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -50,8 +50,12 @@
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
/**
* Force the service to persist, even when it has 0 clients.
- * If setting this flag from the server side, make sure to do so before calling registerService,
- * or there may be a race with the default dynamic shutdown.
+ * If setting this flag from the server side, make sure to do so before calling
+ * registerService, or there may be a race with the default dynamic shutdown.
+ *
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism such as ctl.start.
*/
void forcePersist(bool persist);
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9578372..5aaaa0c 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -50,7 +50,7 @@
class IBinder;
class IPCThreadState;
class ProcessState;
-class RpcConnection;
+class RpcSession;
class String8;
class TextOutput;
@@ -103,7 +103,7 @@
// Whenever possible, markForBinder should be preferred. This method is
// called automatically on reply Parcels for RPC transactions.
- void markForRpc(const sp<RpcConnection>& connection);
+ void markForRpc(const sp<RpcSession>& session);
// Whether this Parcel is written for RPC transactions (after calls to
// markForBinder or markForRpc).
@@ -1136,7 +1136,7 @@
release_func mOwner;
- sp<RpcConnection> mConnection;
+ sp<RpcSession> mSession;
class Blob {
public:
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index 835a3a8..21aa705 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -73,6 +73,8 @@
static bool checkPermission(const String16& permission,
pid_t pid, uid_t uid);
+
+ static void purgeCache();
};
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 0919648..b9db5d7 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -58,6 +58,7 @@
void spawnPooledThread(bool isMain);
status_t setThreadPoolMaxThreadCount(size_t maxThreads);
+ status_t enableOnewaySpamDetection(bool enable);
void giveThreadPoolName();
String8 getDriverName();
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
deleted file mode 100644
index dba47b4..0000000
--- a/libs/binder/include/binder/RpcConnection.h
+++ /dev/null
@@ -1,190 +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
-
-#include <android-base/unique_fd.h>
-#include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <optional>
-#include <vector>
-
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
-namespace android {
-
-class Parcel;
-class RpcServer;
-class RpcState;
-
-/**
- * This represents a multi-threaded/multi-socket connection between a client
- * and a server.
- */
-class RpcConnection final : public virtual RefBase {
-public:
- static sp<RpcConnection> make();
-
- /**
- * This represents a connection for responses, e.g.:
- *
- * process A serves binder a
- * process B opens a connection to process A
- * process B makes binder b and sends it to A
- * A uses this 'back connection' to send things back to B
- *
- * This should be called once, and then a call should be made to join per
- * connection thread.
- */
- [[nodiscard]] bool setupUnixDomainServer(const char* path);
-
- /**
- * This should be called once per thread, matching 'join' in the remote
- * process.
- */
- [[nodiscard]] bool addUnixDomainClient(const char* path);
-
-#ifdef __BIONIC__
- /**
- * Creates an RPC server at the current port.
- */
- [[nodiscard]] bool setupVsockServer(unsigned int port);
-
- /**
- * Connects to an RPC server at the CVD & port.
- */
- [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port);
-#endif // __BIONIC__
-
- /**
- * For debugging!
- *
- * Sets up an empty socket. All queries to this socket which require a
- * response will never be satisfied. All data sent here will be
- * unceremoniously cast down the bottomless pit, /dev/null.
- */
- [[nodiscard]] bool addNullDebuggingClient();
-
- /**
- * Query the other side of the connection for the root object hosted by that
- * process's RpcServer (if one exists)
- */
- sp<IBinder> getRootObject();
-
- [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
- [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
-
- /**
- * Adds a server thread accepting connections. Must be called after
- * setup*Server.
- */
- void join();
-
- ~RpcConnection();
-
- void setForServer(const wp<RpcServer>& server);
- wp<RpcServer> server();
-
- // internal only
- const std::unique_ptr<RpcState>& state() { return mState; }
-
- class SocketAddress {
- public:
- virtual ~SocketAddress();
- virtual std::string toString() const = 0;
- virtual const sockaddr* addr() const = 0;
- virtual size_t addrSize() const = 0;
- };
-
-private:
- friend sp<RpcConnection>;
- RpcConnection();
-
- bool setupSocketServer(const SocketAddress& address);
- bool addSocketClient(const SocketAddress& address);
- void addClient(base::unique_fd&& fd);
- void assignServerToThisThread(base::unique_fd&& fd);
-
- struct ConnectionSocket : public RefBase {
- base::unique_fd fd;
-
- // whether this or another thread is currently using this fd to make
- // or receive transactions.
- std::optional<pid_t> exclusiveTid;
- };
-
- enum class SocketUse {
- CLIENT,
- CLIENT_ASYNC,
- CLIENT_REFCOUNT,
- SERVER,
- };
-
- // RAII object for connection socket
- class ExclusiveSocket {
- public:
- explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use);
- ~ExclusiveSocket();
- const base::unique_fd& fd() { return mSocket->fd; }
-
- private:
- static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
- sp<ConnectionSocket>* available,
- std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint);
-
- sp<RpcConnection> mConnection; // avoid deallocation
- sp<ConnectionSocket> mSocket;
-
- // whether this is being used for a nested transaction (being on the same
- // thread guarantees we won't write in the middle of a message, the way
- // the wire protocol is constructed guarantees this is safe).
- bool mReentrant = false;
- };
-
- // On the other side of a connection, for each of mClients here, there should
- // be one of mServers on the other side (and vice versa).
- //
- // For the simplest connection, a single server with one client, you would
- // have:
- // - the server has a single 'mServers' and a thread listening on this
- // - the client has a single 'mClients' and makes calls to this
- // - here, when the client makes a call, the server can call back into it
- // (nested calls), but outside of this, the client will only ever read
- // calls from the server when it makes a call itself.
- //
- // For a more complicated case, the client might itself open up a thread to
- // serve calls to the server at all times (e.g. if it hosts a callback)
-
- wp<RpcServer> mForServer; // maybe null, for client connections
-
- std::unique_ptr<RpcState> mState;
-
- base::unique_fd mServer; // socket we are accepting connections on
-
- std::mutex mSocketMutex; // for all below
- std::condition_variable mSocketCv; // for mWaitingThreads
- size_t mWaitingThreads = 0;
- size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
- std::vector<sp<ConnectionSocket>> mClients;
- std::vector<sp<ConnectionSocket>> mServers;
-};
-
-} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index d29b651..8f0c6fd 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -17,69 +17,148 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <mutex>
+#include <thread>
+
// WARNING: This is a feature which is still in development, and it is subject
// to radical change. Any production use of this may subject your code to any
// number of problems.
namespace android {
+class RpcSocketAddress;
+
/**
* This represents a server of an interface, which may be connected to by any
* number of clients over sockets.
*
- * This object is not (currently) thread safe. All calls to it are expected to
- * happen at process startup.
+ * Usage:
+ * auto server = RpcServer::make();
+ * // only supports one now
+ * if (!server->setup*Server(...)) {
+ * :(
+ * }
+ * server->join();
*/
class RpcServer final : public virtual RefBase {
public:
static sp<RpcServer> make();
+ /**
+ * This represents a session for responses, e.g.:
+ *
+ * process A serves binder a
+ * process B opens a session to process A
+ * process B makes binder b and sends it to A
+ * A uses this 'back session' to send things back to B
+ */
+ [[nodiscard]] bool setupUnixDomainServer(const char* path);
+
+ /**
+ * Creates an RPC server at the current port.
+ */
+ [[nodiscard]] bool setupVsockServer(unsigned int port);
+
+ /**
+ * Creates an RPC server at the current port using IPv4.
+ *
+ * TODO(b/182914638): IPv6 support
+ *
+ * Set |port| to 0 to pick an ephemeral port; see discussion of
+ * /proc/sys/net/ipv4/ip_local_port_range in ip(7). In this case, |assignedPort|
+ * will be set to the picked port number, if it is not null.
+ */
+ [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
+
+ /**
+ * If setup*Server has been successful, return true. Otherwise return false.
+ */
+ [[nodiscard]] bool hasServer();
+
+ /**
+ * If hasServer(), return the server FD. Otherwise return invalid FD.
+ */
+ [[nodiscard]] base::unique_fd releaseServer();
+
+ /**
+ * Set up server using an external FD previously set up by releaseServer().
+ * Return false if there's already a server.
+ */
+ bool setupExternalServer(base::unique_fd serverFd);
+
void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
/**
- * Setup a static connection, when the number of clients are known.
+ * This must be called before adding a client session.
*
- * Each call to this function corresponds to a different client, and clients
- * each have their own threadpools.
+ * If this is not specified, this will be a single-threaded server.
*
- * TODO(b/167966510): support dynamic creation of connections/threads
+ * TODO(b/185167543): these are currently created per client, but these
+ * should be shared.
*/
- sp<RpcConnection> addClientConnection();
-
- /**
- * Allowing a server to explicitly drop clients would be easy to add here,
- * but it is not currently implemented, since users of this functionality
- * could not use similar functionality if they are running under real
- * binder.
- */
- // void drop(const sp<RpcConnection>& connection);
+ void setMaxThreads(size_t threads);
+ size_t getMaxThreads();
/**
* The root object can be retrieved by any client, without any
- * authentication.
+ * authentication. TODO(b/183988761)
+ *
+ * Holds a strong reference to the root object.
*/
void setRootObject(const sp<IBinder>& binder);
-
/**
- * Root object set with setRootObject
+ * Holds a weak reference to the root object.
*/
+ void setRootObjectWeak(const wp<IBinder>& binder);
sp<IBinder> getRootObject();
+ /**
+ * You must have at least one client session before calling this.
+ *
+ * TODO(b/185167543): way to shut down?
+ */
+ void join();
+
+ /**
+ * Accept one connection on this server. You must have at least one client
+ * session before calling this.
+ */
+ [[nodiscard]] bool acceptOne();
+
+ /**
+ * For debugging!
+ */
+ std::vector<sp<RpcSession>> listSessions();
+ size_t numUninitializedSessions();
+
~RpcServer();
+ // internal use only
+
+ void onSessionTerminating(const sp<RpcSession>& session);
+
private:
friend sp<RpcServer>;
RpcServer();
+ void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd);
+ bool setupSocketServer(const RpcSocketAddress& address);
+
bool mAgreedExperimental = false;
+ bool mStarted = false; // TODO(b/185167543): support dynamically added clients
+ size_t mMaxThreads = 1;
+ base::unique_fd mServer; // socket we are accepting sessions on
+ std::mutex mLock; // for below
+ std::map<std::thread::id, std::thread> mConnectingThreads;
sp<IBinder> mRootObject;
-
- std::vector<sp<RpcConnection>> mConnections; // per-client
+ wp<IBinder> mRootObjectWeak;
+ std::map<int32_t, sp<RpcSession>> mSessions;
+ int32_t mSessionIdCounter = 0;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
new file mode 100644
index 0000000..bcc213c
--- /dev/null
+++ b/libs/binder/include/binder/RpcSession.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcAddress.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <optional>
+#include <thread>
+#include <vector>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+class Parcel;
+class RpcServer;
+class RpcSocketAddress;
+class RpcState;
+
+/**
+ * This represents a session (group of connections) between a client
+ * and a server. Multiple connections are needed for multiple parallel "binder"
+ * calls which may also have nested calls.
+ */
+class RpcSession final : public virtual RefBase {
+public:
+ static sp<RpcSession> make();
+
+ /**
+ * This should be called once per thread, matching 'join' in the remote
+ * process.
+ */
+ [[nodiscard]] bool setupUnixDomainClient(const char* path);
+
+ /**
+ * Connects to an RPC server at the CVD & port.
+ */
+ [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
+
+ /**
+ * Connects to an RPC server at the given address and port.
+ */
+ [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
+
+ /**
+ * For debugging!
+ *
+ * Sets up an empty connection. All queries to this connection which require a
+ * response will never be satisfied. All data sent here will be
+ * unceremoniously cast down the bottomless pit, /dev/null.
+ */
+ [[nodiscard]] bool addNullDebuggingClient();
+
+ /**
+ * Query the other side of the session for the root object hosted by that
+ * process's RpcServer (if one exists)
+ */
+ sp<IBinder> getRootObject();
+
+ /**
+ * Query the other side of the session for the maximum number of threads
+ * it supports (maximum number of concurrent non-nested synchronous transactions)
+ */
+ status_t getRemoteMaxThreads(size_t* maxThreads);
+
+ [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+ [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
+
+ ~RpcSession();
+
+ wp<RpcServer> server();
+
+ // internal only
+ const std::unique_ptr<RpcState>& state() { return mState; }
+
+ class PrivateAccessorForId {
+ private:
+ friend class RpcSession;
+ friend class RpcState;
+ explicit PrivateAccessorForId(const RpcSession* session) : mSession(session) {}
+
+ const std::optional<int32_t> get() { return mSession->mId; }
+
+ const RpcSession* mSession;
+ };
+ PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); }
+
+private:
+ friend PrivateAccessorForId;
+ friend sp<RpcSession>;
+ friend RpcServer;
+ RpcSession();
+
+ status_t readId();
+
+ // transfer ownership of thread
+ void preJoin(std::thread thread);
+ // join on thread passed to preJoin
+ void join(base::unique_fd client);
+ void terminateLocked();
+
+ struct RpcConnection : public RefBase {
+ base::unique_fd fd;
+
+ // whether this or another thread is currently using this fd to make
+ // or receive transactions.
+ std::optional<pid_t> exclusiveTid;
+ };
+
+ bool setupSocketClient(const RpcSocketAddress& address);
+ bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId);
+ void addClientConnection(base::unique_fd fd);
+ void setForServer(const wp<RpcServer>& server, int32_t sessionId);
+ sp<RpcConnection> assignServerToThisThread(base::unique_fd fd);
+ bool removeServerConnection(const sp<RpcConnection>& connection);
+
+ enum class ConnectionUse {
+ CLIENT,
+ CLIENT_ASYNC,
+ CLIENT_REFCOUNT,
+ };
+
+ // RAII object for session connection
+ class ExclusiveConnection {
+ public:
+ explicit ExclusiveConnection(const sp<RpcSession>& session, ConnectionUse use);
+ ~ExclusiveConnection();
+ const base::unique_fd& fd() { return mConnection->fd; }
+
+ private:
+ static void findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ sp<RpcConnection>* available,
+ std::vector<sp<RpcConnection>>& sockets,
+ size_t socketsIndexHint);
+
+ sp<RpcSession> mSession; // avoid deallocation
+ sp<RpcConnection> mConnection;
+
+ // whether this is being used for a nested transaction (being on the same
+ // thread guarantees we won't write in the middle of a message, the way
+ // the wire protocol is constructed guarantees this is safe).
+ bool mReentrant = false;
+ };
+
+ // On the other side of a session, for each of mClientConnections here, there should
+ // be one of mServerConnections on the other side (and vice versa).
+ //
+ // For the simplest session, a single server with one client, you would
+ // have:
+ // - the server has a single 'mServerConnections' and a thread listening on this
+ // - the client has a single 'mClientConnections' and makes calls to this
+ // - here, when the client makes a call, the server can call back into it
+ // (nested calls), but outside of this, the client will only ever read
+ // calls from the server when it makes a call itself.
+ //
+ // For a more complicated case, the client might itself open up a thread to
+ // serve calls to the server at all times (e.g. if it hosts a callback)
+
+ wp<RpcServer> mForServer; // maybe null, for client sessions
+
+ // TODO(b/183988761): this shouldn't be guessable
+ std::optional<int32_t> mId;
+
+ std::unique_ptr<RpcState> mState;
+
+ std::mutex mMutex; // for all below
+
+ std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
+ size_t mWaitingThreads = 0;
+ // hint index into clients, ++ when sending an async transaction
+ size_t mClientConnectionsOffset = 0;
+ std::vector<sp<RpcConnection>> mClientConnections;
+ std::vector<sp<RpcConnection>> mServerConnections;
+
+ // TODO(b/185167543): use for reverse sessions (allow client to also
+ // serve calls on a session).
+ // TODO(b/185167543): allow sharing between different sessions in a
+ // process? (or combine with mServerConnections)
+ std::map<std::thread::id, std::thread> mThreads;
+ bool mTerminated = false;
+};
+
+} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index eb103d3..b03e24c 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -55,7 +55,9 @@
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
- llndk_stubs: "libbinder_ndk.llndk",
+ llndk: {
+ symbol_file: "libbinder_ndk.map.txt",
+ },
export_include_dirs: [
"include_cpp",
@@ -192,13 +194,3 @@
symbol_file: "libbinder_ndk.map.txt",
first_version: "29",
}
-
-llndk_library {
- name: "libbinder_ndk.llndk",
- symbol_file: "libbinder_ndk.map.txt",
- export_include_dirs: [
- "include_cpp",
- "include_ndk",
- "include_platform",
- ],
-}
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 9e2050b..78f2d3a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -150,6 +150,11 @@
/**
* This is called whenever a transaction needs to be processed by a local implementation.
*
+ * This method will be called after the equivalent of
+ * android.os.Parcel#enforceInterface is called. That is, the interface
+ * descriptor associated with the AIBinder_Class descriptor will already be
+ * checked.
+ *
* \param binder the object being transacted on.
* \param code implementation-specific code representing which transaction should be taken.
* \param in the implementation-specific input data to this transaction.
@@ -452,12 +457,14 @@
*/
/**
- * Creates a parcel to start filling out for a transaction. This may add data to the parcel for
- * security, debugging, or other purposes. This parcel is to be sent via AIBinder_transact and it
- * represents the input data to the transaction. It is recommended to check if the object is local
- * and call directly into its user data before calling this as the parceling and unparceling cost
- * can be avoided. This AIBinder must be either built with a class or associated with a class before
- * using this API.
+ * Creates a parcel to start filling out for a transaction. This will add a header to the
+ * transaction that corresponds to android.os.Parcel#writeInterfaceToken. This may add debugging
+ * or other information to the transaction for platform use or to enable other features to work. The
+ * contents of this header is a platform implementation detail, and it is required to use
+ * libbinder_ndk. This parcel is to be sent via AIBinder_transact and it represents the input data
+ * to the transaction. It is recommended to check if the object is local and call directly into its
+ * user data before calling this as the parceling and unparceling cost can be avoided. This AIBinder
+ * must be either built with a class or associated with a class before using this API.
*
* This does not affect the ownership of binder. When this function succeeds, the in parcel's
* ownership is passed to the caller. At this point, the parcel can be filled out and passed to
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 5516914..2a66941 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -35,7 +35,7 @@
* \return EX_NONE on success.
*/
__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addService(
- AIBinder* binder, const char* instance);
+ AIBinder* binder, const char* instance) __INTRODUCED_IN(29);
/**
* Gets a binder object with this specific instance name. Will return nullptr immediately if the
@@ -47,7 +47,8 @@
*
* \param instance identifier of the service used to lookup the service.
*/
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance)
+ __INTRODUCED_IN(29);
/**
* Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
@@ -59,7 +60,8 @@
*
* \param instance identifier of the service used to lookup the service.
*/
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
+ __INTRODUCED_IN(29);
/**
* Registers a lazy service with the default service manager under the 'instance' name.
@@ -124,8 +126,21 @@
__INTRODUCED_IN(31);
/**
+ * Check if a service is updatable via an APEX module.
+ *
+ * \param instance identifier of the service
+ *
+ * \return whether the interface is updatable via APEX
+ */
+bool AServiceManager_isUpdatableViaApex(const char* instance) __INTRODUCED_IN(31);
+
+/**
* Prevent lazy services without client from shutting down their process
*
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism.
+ *
* \param persist 'true' if the process should not exit.
*/
void AServiceManager_forceLazyServicesPersist(bool persist) __INTRODUCED_IN(31);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 67c85b6..7d4b82e 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -118,14 +118,15 @@
AIBinder_getCallingSid; # apex
AIBinder_setRequestingSid; # apex
AParcel_markSensitive; # llndk
- AServiceManager_isDeclared; # apex llndk
AServiceManager_forEachDeclaredInstance; # apex llndk
- AServiceManager_registerLazyService; # llndk
- AServiceManager_waitForService; # apex llndk
AServiceManager_forceLazyServicesPersist; # llndk
+ AServiceManager_isDeclared; # apex llndk
+ AServiceManager_isUpdatableViaApex; # apex
+ AServiceManager_reRegister; # llndk
+ AServiceManager_registerLazyService; # llndk
AServiceManager_setActiveServicesCallback; # llndk
AServiceManager_tryUnregister; # llndk
- AServiceManager_reRegister; # llndk
+ AServiceManager_waitForService; # apex llndk
AIBinder_forceDowngradeToSystemStability; # apex
AIBinder_forceDowngradeToVendorStability; # llndk
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 1ccd0d2..7649a26 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -105,6 +105,14 @@
callback(String8(instance).c_str(), context);
}
}
+bool AServiceManager_isUpdatableViaApex(const char* instance) {
+ if (instance == nullptr) {
+ return false;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ return sm->updatableViaApex(String16(instance)) != std::nullopt;
+}
void AServiceManager_forceLazyServicesPersist(bool persist) {
auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
serviceRegistrar.forcePersist(persist);
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 6a88401..62db3cf 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -300,6 +300,11 @@
EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
}
+TEST(NdkBinder, IsUpdatable) {
+ bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.light.ILights/default");
+ EXPECT_EQ(isUpdatable, false);
+}
+
// This is too slow
TEST(NdkBinder, CheckLazyServiceShutDown) {
ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 57c9013..49d3401 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -50,6 +50,8 @@
"//apex_available:platform",
"com.android.virt",
],
+ lints: "none",
+ clippy_lints: "none",
}
rust_bindgen {
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 321b422..695a83e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -548,6 +548,28 @@
}
}
+/// The features to enable when creating a native Binder.
+///
+/// This should always be initialised with a default value, e.g.:
+/// ```
+/// # use binder::BinderFeatures;
+/// BinderFeatures {
+/// set_requesting_sid: true,
+/// ..BinderFeatures::default(),
+/// }
+/// ```
+#[derive(Clone, Debug, Default, Eq, PartialEq)]
+pub struct BinderFeatures {
+ /// Indicates that the service intends to receive caller security contexts. This must be true
+ /// for `ThreadState::with_calling_sid` to work.
+ 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
+ // expressions entirely.
+ #[doc(hidden)]
+ pub _non_exhaustive: (),
+}
+
/// Declare typed interfaces for a binder object.
///
/// Given an interface trait and descriptor string, create a native and remote
@@ -730,8 +752,9 @@
impl $native {
/// Create a new binder service.
- pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> {
- let binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+ 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);
+ $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 30928a5..2694cba 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,10 +107,9 @@
pub mod parcel;
pub use crate::binder::{
- 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,
+ 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,
};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
pub use native::add_service;
@@ -125,8 +124,8 @@
pub use super::parcel::ParcelFileDescriptor;
pub use super::{add_service, get_interface};
pub use super::{
- DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder, Status,
- StatusCode, Strong, ThreadState, Weak, WpIBinder,
+ BinderFeatures, 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/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 9095af2..1d1a295 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -16,14 +16,6 @@
//! Generated Rust bindings to libbinder_ndk
-#![allow(
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- unused,
- improper_ctypes,
- missing_docs
-)]
use std::error::Error;
use std::fmt;
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 60e3502..0332007 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -19,7 +19,7 @@
use binder::declare_binder_interface;
use binder::parcel::Parcel;
use binder::{
- Binder, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
+ Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
FIRST_CALL_TRANSACTION,
};
use std::convert::{TryFrom, TryInto};
@@ -55,7 +55,8 @@
})));
service.set_requesting_sid(true);
if let Some(extension_name) = extension_name {
- let extension = BnTest::new_binder(TestService { s: extension_name });
+ let extension =
+ BnTest::new_binder(TestService { s: extension_name }, BinderFeatures::default());
service
.set_extension(&mut extension.as_binder())
.expect("Could not add extension");
@@ -212,8 +213,8 @@
use std::time::Duration;
use binder::{
- Binder, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface, SpIBinder,
- StatusCode, Strong,
+ Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
+ SpIBinder, StatusCode, Strong,
};
use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
@@ -495,9 +496,12 @@
#[test]
fn reassociate_rust_binder() {
let service_name = "testing_service";
- let service_ibinder = BnTest::new_binder(TestService {
- s: service_name.to_string(),
- })
+ let service_ibinder = BnTest::new_binder(
+ TestService {
+ s: service_name.to_string(),
+ },
+ BinderFeatures::default(),
+ )
.as_binder();
let service: Strong<dyn ITest> = service_ibinder
@@ -510,9 +514,12 @@
#[test]
fn weak_binder_upgrade() {
let service_name = "testing_service";
- let service = BnTest::new_binder(TestService {
- s: service_name.to_string(),
- });
+ let service = BnTest::new_binder(
+ TestService {
+ s: service_name.to_string(),
+ },
+ BinderFeatures::default(),
+ );
let weak = Strong::downgrade(&service);
@@ -525,9 +532,12 @@
fn weak_binder_upgrade_dead() {
let service_name = "testing_service";
let weak = {
- let service = BnTest::new_binder(TestService {
- s: service_name.to_string(),
- });
+ let service = BnTest::new_binder(
+ TestService {
+ s: service_name.to_string(),
+ },
+ BinderFeatures::default(),
+ );
Strong::downgrade(&service)
};
@@ -538,9 +548,12 @@
#[test]
fn weak_binder_clone() {
let service_name = "testing_service";
- let service = BnTest::new_binder(TestService {
- s: service_name.to_string(),
- });
+ let service = BnTest::new_binder(
+ TestService {
+ s: service_name.to_string(),
+ },
+ BinderFeatures::default(),
+ );
let weak = Strong::downgrade(&service);
let cloned = weak.clone();
@@ -556,12 +569,18 @@
#[test]
#[allow(clippy::eq_op)]
fn binder_ord() {
- let service1 = BnTest::new_binder(TestService {
- s: "testing_service1".to_string(),
- });
- let service2 = BnTest::new_binder(TestService {
- s: "testing_service2".to_string(),
- });
+ let service1 = BnTest::new_binder(
+ TestService {
+ s: "testing_service1".to_string(),
+ },
+ BinderFeatures::default(),
+ );
+ let service2 = BnTest::new_binder(
+ TestService {
+ s: "testing_service2".to_string(),
+ },
+ BinderFeatures::default(),
+ );
assert!(!(service1 < service1));
assert!(!(service1 > service1));
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index ce75ab7..4702e45 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -16,15 +16,13 @@
//! Rust Binder NDK interop tests
-use std::ffi::CStr;
-use std::os::raw::{c_char, c_int};
-use ::IBinderRustNdkInteropTest::binder::{self, Interface, StatusCode};
use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTest::{
BnBinderRustNdkInteropTest, IBinderRustNdkInteropTest,
};
-use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTestOther::{
- IBinderRustNdkInteropTestOther,
-};
+use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTestOther::IBinderRustNdkInteropTestOther;
+use ::IBinderRustNdkInteropTest::binder::{self, BinderFeatures, Interface, StatusCode};
+use std::ffi::CStr;
+use std::os::raw::{c_char, c_int};
/// Look up the provided AIDL service and call its echo method.
///
@@ -37,18 +35,21 @@
// The Rust class descriptor pointer will not match the NDK one, but the
// descriptor strings match so this needs to still associate.
- let service: binder::Strong<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
- Err(e) => {
- eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
- return StatusCode::NAME_NOT_FOUND as c_int;
- }
- Ok(service) => service,
- };
+ let service: binder::Strong<dyn IBinderRustNdkInteropTest> =
+ match binder::get_interface(service_name) {
+ Err(e) => {
+ eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
+ return StatusCode::NAME_NOT_FOUND as c_int;
+ }
+ Ok(service) => service,
+ };
match service.echo("testing") {
- Ok(s) => if s != "testing" {
- return StatusCode::BAD_VALUE as c_int;
- },
+ Ok(s) => {
+ if s != "testing" {
+ return StatusCode::BAD_VALUE as c_int;
+ }
+ }
Err(e) => return e.into(),
}
@@ -88,7 +89,7 @@
#[no_mangle]
pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int {
let service_name = CStr::from_ptr(service_name).to_str().unwrap();
- let service = BnBinderRustNdkInteropTest::new_binder(Service);
+ let service = BnBinderRustNdkInteropTest::new_binder(Service, BinderFeatures::default());
match binder::add_service(&service_name, service.as_binder()) {
Ok(_) => StatusCode::OK as c_int,
Err(e) => e as c_int,
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index f1b068e..66ba846 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -18,11 +18,11 @@
//! access.
use binder::declare_binder_interface;
+use binder::parcel::ParcelFileDescriptor;
use binder::{
- Binder, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
+ Binder, BinderFeatures, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
StatusCode, TransactionCode,
};
-use binder::parcel::ParcelFileDescriptor;
use std::ffi::{c_void, CStr, CString};
use std::sync::Once;
@@ -85,7 +85,7 @@
pub extern "C" fn rust_service() -> *mut c_void {
unsafe {
SERVICE_ONCE.call_once(|| {
- SERVICE = Some(BnReadParcelTest::new_binder(()).as_binder());
+ SERVICE = Some(BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder());
});
SERVICE.as_ref().unwrap().as_raw().cast()
}
@@ -108,8 +108,12 @@
impl ReadParcelTest for () {}
#[allow(clippy::float_cmp)]
-fn on_transact(_service: &dyn ReadParcelTest, code: TransactionCode,
- parcel: &Parcel, reply: &mut Parcel) -> Result<()> {
+fn on_transact(
+ _service: &dyn ReadParcelTest,
+ code: TransactionCode,
+ parcel: &Parcel,
+ reply: &mut Parcel,
+) -> Result<()> {
match code {
bindings::Transaction_TEST_BOOL => {
assert_eq!(parcel.read::<bool>()?, true);
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index f303b7c..ec231b2 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -35,6 +35,7 @@
name: "binderDriverInterfaceTest_IPC_32",
defaults: ["binder_test_defaults"],
srcs: ["binderDriverInterfaceTest.cpp"],
+ header_libs: ["libbinder_headers"],
compile_multilib: "32",
multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -49,7 +50,7 @@
cflags: ["-DBINDER_IPC_32BIT=1"],
},
},
-
+ header_libs: ["libbinder_headers"],
srcs: ["binderDriverInterfaceTest.cpp"],
test_suites: ["device-tests", "vts"],
}
@@ -62,6 +63,9 @@
"libbinder",
"libutils",
],
+ static_libs: [
+ "libgmock",
+ ],
compile_multilib: "32",
multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -100,6 +104,9 @@
"libbinder",
"libutils",
],
+ static_libs: [
+ "libgmock",
+ ],
test_suites: ["device-tests", "vts"],
require_root: true,
}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 2bdb264..ef4198d 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -18,8 +18,8 @@
oneway void sendString(@utf8InCpp String str);
@utf8InCpp String doubleString(@utf8InCpp String str);
- // number of known RPC binders to process, RpcState::countBinders
- int countBinders();
+ // number of known RPC binders to process, RpcState::countBinders by session
+ int[] countBinders();
// Caller sends server, callee pings caller's server and returns error code.
int pingMe(IBinder binder);
@@ -36,7 +36,7 @@
// should always return the same binder
IBinder alwaysGiveMeTheSameBinder();
- // Idea is that the server will not hold onto the session, the remote connection
+ // Idea is that the server will not hold onto the session, the remote session
// object must. This is to test lifetimes of binder objects, and consequently, also
// identity (since by assigning sessions names, we can make sure a section always
// references the session it was originally opened with).
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e2193fa..0c3fbcd 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <thread>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <binder/Binder.h>
@@ -31,16 +32,23 @@
#include <binder/IServiceManager.h>
#include <binder/ParcelRef.h>
-#include <private/binder/binder_module.h>
#include <linux/sched.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
+#include "../binder_module.h"
#include "binderAbiHelper.h"
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
+using testing::Not;
+
+// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
+MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
+ *result_listener << statusToString(arg);
+ return expected == arg;
+}
static ::testing::AssertionResult IsPageAligned(void *buf) {
if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
@@ -88,6 +96,7 @@
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_REJECT_BUF,
+ BINDER_LIB_TEST_CAN_GET_SID,
};
pid_t start_server_process(int arg2, bool usePoll = false)
@@ -204,19 +213,16 @@
protected:
sp<IBinder> addServerEtc(int32_t *idPtr, int code)
{
- int ret;
int32_t id;
Parcel data, reply;
sp<IBinder> binder;
- ret = m_server->transact(code, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR));
EXPECT_FALSE(binder != nullptr);
binder = reply.readStrongBinder();
EXPECT_TRUE(binder != nullptr);
- ret = reply.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
if (idPtr)
*idPtr = id;
return binder;
@@ -400,29 +406,25 @@
};
TEST_F(BinderLibTest, NopTransaction) {
- status_t ret;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, NopTransactionOneway) {
- status_t ret;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, NopTransactionClear) {
- status_t ret;
Parcel data, reply;
// make sure it accepts the transaction flag
- ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, Freeze) {
- status_t ret;
Parcel data, reply, replypid;
std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
@@ -441,9 +443,8 @@
return;
}
- ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
int32_t pid = replypid.readInt32();
- EXPECT_EQ(NO_ERROR, ret);
for (int i = 0; i < 10; i++) {
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
}
@@ -467,42 +468,36 @@
TEST_F(BinderLibTest, SetError) {
int32_t testValue[] = { 0, -123, 123 };
for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) {
- status_t ret;
Parcel data, reply;
data.writeInt32(testValue[i]);
- ret = m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply);
- EXPECT_EQ(testValue[i], ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply),
+ StatusEq(testValue[i]));
}
}
TEST_F(BinderLibTest, GetId) {
- status_t ret;
int32_t id;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
- ret = reply.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
+ EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
}
TEST_F(BinderLibTest, PtrSize) {
- status_t ret;
int32_t ptrsize;
Parcel data, reply;
sp<IBinder> server = addServer();
ASSERT_TRUE(server != nullptr);
- ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
- ret = reply.readInt32(&ptrsize);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
+ EXPECT_THAT(reply.readInt32(&ptrsize), StatusEq(NO_ERROR));
RecordProperty("TestPtrSize", sizeof(void *));
RecordProperty("ServerPtrSize", sizeof(void *));
}
TEST_F(BinderLibTest, IndirectGetId2)
{
- status_t ret;
int32_t id;
int32_t count;
Parcel data, reply;
@@ -520,22 +515,19 @@
datai.appendTo(&data);
}
- ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
- ret = reply.readInt32(&id);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
- ret = reply.readInt32(&count);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
BinderLibTestBundle replyi(&reply);
EXPECT_TRUE(replyi.isValid());
- ret = replyi.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(serverId[i], id);
EXPECT_EQ(replyi.dataSize(), replyi.dataPosition());
}
@@ -545,7 +537,6 @@
TEST_F(BinderLibTest, IndirectGetId3)
{
- status_t ret;
int32_t id;
int32_t count;
Parcel data, reply;
@@ -570,15 +561,13 @@
datai.appendTo(&data);
}
- ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
- ret = reply.readInt32(&id);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
- ret = reply.readInt32(&count);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
@@ -586,18 +575,15 @@
BinderLibTestBundle replyi(&reply);
EXPECT_TRUE(replyi.isValid());
- ret = replyi.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(serverId[i], id);
- ret = replyi.readInt32(&counti);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(replyi.readInt32(&counti), StatusEq(NO_ERROR));
EXPECT_EQ(1, counti);
BinderLibTestBundle replyi2(&replyi);
EXPECT_TRUE(replyi2.isValid());
- ret = replyi2.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(replyi2.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
EXPECT_EQ(replyi2.dataSize(), replyi2.dataPosition());
@@ -609,16 +595,13 @@
TEST_F(BinderLibTest, CallBack)
{
- status_t ret;
Parcel data, reply;
sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
data.writeStrongBinder(callBack);
- ret = m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, AddServer)
@@ -629,7 +612,6 @@
TEST_F(BinderLibTest, DeathNotificationStrongRef)
{
- status_t ret;
sp<IBinder> sbinder;
sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
@@ -637,20 +619,17 @@
{
sp<IBinder> binder = addServer();
ASSERT_TRUE(binder != nullptr);
- ret = binder->linkToDeath(testDeathRecipient);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(binder->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
sbinder = binder;
}
{
Parcel data, reply;
- ret = sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(0, ret);
+ EXPECT_THAT(sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY),
+ StatusEq(OK));
}
IPCThreadState::self()->flushCommands();
- ret = testDeathRecipient->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = sbinder->unlinkToDeath(testDeathRecipient);
- EXPECT_EQ(DEAD_OBJECT, ret);
+ EXPECT_THAT(testDeathRecipient->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(sbinder->unlinkToDeath(testDeathRecipient), StatusEq(DEAD_OBJECT));
}
TEST_F(BinderLibTest, DeathNotificationMultiple)
@@ -673,8 +652,9 @@
callBack[i] = new BinderLibTestCallBack();
data.writeStrongBinder(target);
data.writeStrongBinder(callBack[i]);
- ret = linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data,
+ &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
{
Parcel data, reply;
@@ -682,8 +662,9 @@
passiveclient[i] = addServer();
ASSERT_TRUE(passiveclient[i] != nullptr);
data.writeStrongBinder(target);
- ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data,
+ &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
}
{
@@ -693,10 +674,8 @@
}
for (int i = 0; i < clientcount; i++) {
- ret = callBack[i]->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack[i]->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callBack[i]->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack[i]->getResult(), StatusEq(NO_ERROR));
}
}
@@ -711,8 +690,7 @@
sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
- ret = target->linkToDeath(testDeathRecipient);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(target->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
{
Parcel data, reply;
@@ -749,14 +727,13 @@
callback = new BinderLibTestCallBack();
data.writeStrongBinder(target);
data.writeStrongBinder(callback);
- ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply,
+ TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
- ret = callback->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callback->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callback->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, PassFile) {
@@ -772,17 +749,14 @@
Parcel data, reply;
uint8_t writebuf[1] = { write_value };
- ret = data.writeFileDescriptor(pipefd[1], true);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(data.writeFileDescriptor(pipefd[1], true), StatusEq(NO_ERROR));
- ret = data.writeInt32(sizeof(writebuf));
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(data.writeInt32(sizeof(writebuf)), StatusEq(NO_ERROR));
- ret = data.write(writebuf, sizeof(writebuf));
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(data.write(writebuf, sizeof(writebuf)), StatusEq(NO_ERROR));
- ret = m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
}
ret = read(pipefd[0], buf, sizeof(buf));
@@ -863,11 +837,10 @@
}
TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
- status_t ret;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
const flat_binder_object *fb = reply.readObject(false);
ASSERT_TRUE(fb != nullptr);
@@ -887,8 +860,8 @@
wp<IBinder> keepFreedBinder;
{
Parcel data, reply;
- ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
freedHandle = freed->handle;
/* Add a weak ref to the freed binder so the driver does not
@@ -949,7 +922,6 @@
}
TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
- status_t ret;
Parcel data, reply;
sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
for (int i = 0; i < 2; i++) {
@@ -963,13 +935,12 @@
datai.appendTo(&data);
}
- ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, OnewayQueueing)
{
- status_t ret;
Parcel data, data2;
sp<IBinder> pollServer = addPollServer();
@@ -982,25 +953,21 @@
data2.writeStrongBinder(callBack2);
data2.writeInt32(0); // delay in us
- ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
// The delay ensures that this second transaction will end up on the async_todo list
// (for a single-threaded server)
- ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
// The server will ensure that the two transactions are handled in the expected order;
// If the ordering is not as expected, an error will be returned through the callbacks.
- ret = callBack->waitEvent(2);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callBack->waitEvent(2), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
- ret = callBack2->waitEvent(2);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack2->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callBack2->waitEvent(2), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack2->getResult(), StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, WorkSourceUnsetByDefault)
@@ -1119,8 +1086,8 @@
ASSERT_TRUE(server != nullptr);
Parcel data, reply;
- status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+ StatusEq(NO_ERROR));
int policy = reply.readInt32();
int priority = reply.readInt32();
@@ -1139,8 +1106,8 @@
EXPECT_EQ(0, sched_setscheduler(getpid(), SCHED_RR, ¶m));
Parcel data, reply;
- status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+ StatusEq(NO_ERROR));
int policy = reply.readInt32();
int priority = reply.readInt32();
@@ -1157,10 +1124,9 @@
std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 };
data.writeUint64Vector(testValue);
- status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR));
std::vector<uint64_t> readValue;
- ret = reply.readUint64Vector(&readValue);
+ EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK));
EXPECT_EQ(readValue, testValue);
}
@@ -1185,11 +1151,18 @@
memcpy(parcelData, &obj, sizeof(obj));
data.setDataSize(sizeof(obj));
- status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply);
// 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_NE(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply),
+ Not(StatusEq(NO_ERROR)));
+}
+
+TEST_F(BinderLibTest, GotSid) {
+ sp<IBinder> server = addServer();
+
+ Parcel data;
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
}
class BinderLibTestService : public BBinder
@@ -1494,6 +1467,9 @@
case BINDER_LIB_TEST_REJECT_BUF: {
return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
}
+ case BINDER_LIB_TEST_CAN_GET_SID: {
+ return IPCThreadState::self()->getCallingSid() == nullptr ? BAD_VALUE : NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 7c82226..a457e67 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -18,8 +18,8 @@
#include <android-base/logging.h>
#include <benchmark/benchmark.h>
#include <binder/Binder.h>
-#include <binder/RpcConnection.h>
#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <thread>
@@ -30,8 +30,8 @@
using android::IBinder;
using android::interface_cast;
using android::OK;
-using android::RpcConnection;
using android::RpcServer;
+using android::RpcSession;
using android::sp;
using android::binder::Status;
@@ -46,17 +46,17 @@
}
};
-static sp<RpcConnection> gConnection = RpcConnection::make();
+static sp<RpcSession> gSession = RpcSession::make();
void BM_getRootObject(benchmark::State& state) {
while (state.KeepRunning()) {
- CHECK(gConnection->getRootObject() != nullptr);
+ CHECK(gSession->getRootObject() != nullptr);
}
}
BENCHMARK(BM_getRootObject);
void BM_pingTransaction(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
while (state.KeepRunning()) {
@@ -66,7 +66,7 @@
BENCHMARK(BM_pingTransaction);
void BM_repeatString(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -95,7 +95,7 @@
BENCHMARK(BM_repeatString);
void BM_repeatBinder(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -121,21 +121,18 @@
std::thread([addr]() {
sp<RpcServer> server = RpcServer::make();
server->setRootObject(sp<MyBinderRpcBenchmark>::make());
-
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
- sp<RpcConnection> connection = server->addClientConnection();
- CHECK(connection->setupUnixDomainServer(addr.c_str()));
-
- connection->join();
+ CHECK(server->setupUnixDomainServer(addr.c_str()));
+ server->join();
}).detach();
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
- if (gConnection->addUnixDomainClient(addr.c_str())) goto success;
+ if (gSession->setupUnixDomainClient(addr.c_str())) goto success;
}
LOG(FATAL) << "Could not connect.";
success:
::benchmark::RunSpecifiedBenchmarks();
+ return 0;
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 3340293..a96deb5 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -17,6 +17,7 @@
#include <BnBinderRpcSession.h>
#include <BnBinderRpcTest.h>
#include <aidl/IBinderRpcTest.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
@@ -24,8 +25,8 @@
#include <binder/BpBinder.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
-#include <binder/RpcConnection.h>
#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <gtest/gtest.h>
#include <chrono>
@@ -33,14 +34,11 @@
#include <iostream>
#include <thread>
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif //__BIONIC__
-
#include <sys/prctl.h>
#include <unistd.h>
-#include "../RpcState.h" // for debugging
+#include "../RpcState.h" // for debugging
+#include "../vm_sockets.h" // for VMADDR_*
namespace android {
@@ -51,6 +49,19 @@
EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
}
+TEST(BinderRpc, SetExternalServer) {
+ base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+ int sinkFd = sink.get();
+ auto server = RpcServer::make();
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ ASSERT_FALSE(server->hasServer());
+ ASSERT_TRUE(server->setupExternalServer(std::move(sink)));
+ ASSERT_TRUE(server->hasServer());
+ base::unique_fd retrieved = server->releaseServer();
+ ASSERT_FALSE(server->hasServer());
+ ASSERT_EQ(sinkFd, retrieved.get());
+}
+
using android::binder::Status;
#define EXPECT_OK(status) \
@@ -77,24 +88,30 @@
class MyBinderRpcTest : public BnBinderRpcTest {
public:
- sp<RpcConnection> connection;
+ wp<RpcServer> server;
Status sendString(const std::string& str) override {
- std::cout << "Child received string: " << str << std::endl;
+ (void)str;
return Status::ok();
}
Status doubleString(const std::string& str, std::string* strstr) override {
- std::cout << "Child received string to double: " << str << std::endl;
*strstr = str + str;
return Status::ok();
}
- Status countBinders(int32_t* out) override {
- if (connection == nullptr) {
+ Status countBinders(std::vector<int32_t>* out) override {
+ sp<RpcServer> spServer = server.promote();
+ if (spServer == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
- *out = connection->state()->countBinders();
- if (*out != 1) {
- connection->state()->dump();
+ out->clear();
+ for (auto session : spServer->listSessions()) {
+ size_t count = session->state()->countBinders();
+ if (count != 1) {
+ // this is called when there is only one binder held remaining,
+ // so to aid debugging
+ session->state()->dump();
+ }
+ out->push_back(count);
}
return Status::ok();
}
@@ -177,14 +194,27 @@
};
sp<IBinder> MyBinderRpcTest::mHeldBinder;
+class Pipe {
+public:
+ Pipe() { CHECK(android::base::Pipe(&mRead, &mWrite)); }
+ Pipe(Pipe&&) = default;
+ android::base::borrowed_fd readEnd() { return mRead; }
+ android::base::borrowed_fd writeEnd() { return mWrite; }
+
+private:
+ android::base::unique_fd mRead;
+ android::base::unique_fd mWrite;
+};
+
class Process {
public:
- Process(const std::function<void()>& f) {
+ Process(Process&&) = default;
+ Process(const std::function<void(Pipe*)>& f) {
if (0 == (mPid = fork())) {
// racey: assume parent doesn't crash before this is set
prctl(PR_SET_PDEATHSIG, SIGHUP);
- f();
+ f(&mPipe);
}
}
~Process() {
@@ -192,9 +222,11 @@
kill(mPid, SIGKILL);
}
}
+ Pipe* getPipe() { return &mPipe; }
private:
pid_t mPid = 0;
+ Pipe mPipe;
};
static std::string allocateSocketAddress() {
@@ -203,48 +235,63 @@
return temp + "/binderRpcTest_" + std::to_string(id++);
};
-struct ProcessConnection {
+struct ProcessSession {
// reference to process hosting a socket server
Process host;
- // client connection object associated with other process
- sp<RpcConnection> connection;
+ struct SessionInfo {
+ sp<RpcSession> session;
+ sp<IBinder> root;
+ };
- // pre-fetched root object
- sp<IBinder> rootBinder;
+ // client session objects associated with other process
+ // each one represents a separate session
+ std::vector<SessionInfo> sessions;
- // whether connection should be invalidated by end of run
- bool expectInvalid = false;
+ ProcessSession(ProcessSession&&) = default;
+ ~ProcessSession() {
+ for (auto& session : sessions) {
+ session.root = nullptr;
+ }
- ~ProcessConnection() {
- rootBinder = nullptr;
- EXPECT_NE(nullptr, connection);
- EXPECT_NE(nullptr, connection->state());
- EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:");
+ for (auto& info : sessions) {
+ sp<RpcSession>& session = info.session;
- wp<RpcConnection> weakConnection = connection;
- connection = nullptr;
- EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+ EXPECT_NE(nullptr, session);
+ EXPECT_NE(nullptr, session->state());
+ EXPECT_EQ(0, session->state()->countBinders()) << (session->state()->dump(), "dump:");
+
+ wp<RpcSession> weakSession = session;
+ session = nullptr;
+ EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session";
+ }
}
};
-// Process connection where the process hosts IBinderRpcTest, the server used
+// Process session where the process hosts IBinderRpcTest, the server used
// for most testing here
-struct BinderRpcTestProcessConnection {
- ProcessConnection proc;
+struct BinderRpcTestProcessSession {
+ ProcessSession proc;
- // pre-fetched root object
+ // pre-fetched root object (for first session)
sp<IBinder> rootBinder;
- // pre-casted root object
+ // pre-casted root object (for first session)
sp<IBinderRpcTest> rootIface;
- ~BinderRpcTestProcessConnection() {
- if (!proc.expectInvalid) {
- int32_t remoteBinders = 0;
- EXPECT_OK(rootIface->countBinders(&remoteBinders));
- // should only be the root binder object, iface
- EXPECT_EQ(remoteBinders, 1);
+ // whether session should be invalidated by end of run
+ bool expectInvalid = false;
+
+ BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
+ ~BinderRpcTestProcessSession() {
+ if (!expectInvalid) {
+ std::vector<int32_t> remoteCounts;
+ // calling over any sessions counts across all sessions
+ EXPECT_OK(rootIface->countBinders(&remoteCounts));
+ EXPECT_EQ(remoteCounts.size(), proc.sessions.size());
+ for (auto remoteCount : remoteCounts) {
+ EXPECT_EQ(remoteCount, 1);
+ }
}
rootIface = nullptr;
@@ -254,18 +301,17 @@
enum class SocketType {
UNIX,
-#ifdef __BIONIC__
VSOCK,
-#endif // __BIONIC__
+ INET,
};
static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
switch (info.param) {
case SocketType::UNIX:
return "unix_domain_socket";
-#ifdef __BIONIC__
case SocketType::VSOCK:
return "vm_socket";
-#endif // __BIONIC__
+ case SocketType::INET:
+ return "inet_socket";
default:
LOG_ALWAYS_FATAL("Unknown socket type");
return "";
@@ -275,92 +321,93 @@
public:
// This creates a new process serving an interface on a certain number of
// threads.
- ProcessConnection createRpcTestSocketServerProcess(
- size_t numThreads,
- const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
- CHECK_GT(numThreads, 0);
+ ProcessSession createRpcTestSocketServerProcess(
+ size_t numThreads, size_t numSessions,
+ const std::function<void(const sp<RpcServer>&)>& configure) {
+ CHECK_GE(numSessions, 1) << "Must have at least one session to a server";
SocketType socketType = GetParam();
std::string addr = allocateSocketAddress();
unlink(addr.c_str());
- static unsigned int port = 3456;
- port++;
+ static unsigned int vsockPort = 3456;
+ vsockPort++;
- auto ret = ProcessConnection{
- .host = Process([&] {
+ auto ret = ProcessSession{
+ .host = Process([&](Pipe* pipe) {
sp<RpcServer> server = RpcServer::make();
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ server->setMaxThreads(numThreads);
- // server supporting one client on one socket
- sp<RpcConnection> connection = server->addClientConnection();
+ unsigned int outPort = 0;
switch (socketType) {
case SocketType::UNIX:
- CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+ CHECK(server->setupUnixDomainServer(addr.c_str())) << addr;
break;
-#ifdef __BIONIC__
case SocketType::VSOCK:
- CHECK(connection->setupVsockServer(port));
+ CHECK(server->setupVsockServer(vsockPort));
break;
-#endif // __BIONIC__
+ case SocketType::INET: {
+ CHECK(server->setupInetServer(0, &outPort));
+ CHECK_NE(0, outPort);
+ break;
+ }
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
- configure(server, connection);
+ CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort, sizeof(outPort)));
- // accept 'numThreads' connections
- std::vector<std::thread> pool;
- for (size_t i = 0; i + 1 < numThreads; i++) {
- pool.push_back(std::thread([=] { connection->join(); }));
- }
- connection->join();
- for (auto& t : pool) t.join();
+ configure(server);
+
+ server->join();
}),
- .connection = RpcConnection::make(),
};
- // create remainder of connections
- for (size_t i = 0; i < numThreads; i++) {
- for (size_t tries = 0; tries < 5; tries++) {
- usleep(10000);
- switch (socketType) {
- case SocketType::UNIX:
- if (ret.connection->addUnixDomainClient(addr.c_str())) goto success;
- break;
-#ifdef __BIONIC__
- case SocketType::VSOCK:
- if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success;
- break;
-#endif // __BIONIC__
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- }
- }
- LOG_ALWAYS_FATAL("Could not connect");
- success:;
+ // always read socket, so that we have waited for the server to start
+ unsigned int outPort = 0;
+ CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &outPort, sizeof(outPort)));
+ if (socketType == SocketType::INET) {
+ CHECK_NE(0, outPort);
}
- ret.rootBinder = ret.connection->getRootObject();
+ for (size_t i = 0; i < numSessions; i++) {
+ sp<RpcSession> session = RpcSession::make();
+ switch (socketType) {
+ case SocketType::UNIX:
+ if (session->setupUnixDomainClient(addr.c_str())) goto success;
+ break;
+ case SocketType::VSOCK:
+ if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
+ break;
+ case SocketType::INET:
+ if (session->setupInetClient("127.0.0.1", outPort)) goto success;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
+ LOG_ALWAYS_FATAL("Could not connect");
+ success:
+ ret.sessions.push_back({session, session->getRootObject()});
+ }
return ret;
}
- BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
- BinderRpcTestProcessConnection ret{
- .proc = createRpcTestSocketServerProcess(numThreads,
- [&](const sp<RpcServer>& server,
- const sp<RpcConnection>& connection) {
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(size_t numThreads,
+ size_t numSessions = 1) {
+ BinderRpcTestProcessSession ret{
+ .proc = createRpcTestSocketServerProcess(numThreads, numSessions,
+ [&](const sp<RpcServer>& server) {
sp<MyBinderRpcTest> service =
new MyBinderRpcTest;
server->setRootObject(service);
- service->connection =
- connection; // for testing only
+ service->server = server;
}),
};
- ret.rootBinder = ret.proc.rootBinder;
+ ret.rootBinder = ret.proc.sessions.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
return ret;
@@ -368,18 +415,12 @@
};
TEST_P(BinderRpc, RootObjectIsNull) {
- auto proc = createRpcTestSocketServerProcess(1,
- [](const sp<RpcServer>& server,
- const sp<RpcConnection>&) {
- // this is the default, but to be explicit
- server->setRootObject(nullptr);
- });
+ auto proc = createRpcTestSocketServerProcess(1, 1, [](const sp<RpcServer>& server) {
+ // this is the default, but to be explicit
+ server->setRootObject(nullptr);
+ });
- // retrieved by getRootObject when process is created above
- EXPECT_EQ(nullptr, proc.rootBinder);
-
- // make sure we can retrieve it again (process doesn't crash)
- EXPECT_EQ(nullptr, proc.connection->getRootObject());
+ EXPECT_EQ(nullptr, proc.sessions.at(0).root);
}
TEST_P(BinderRpc, Ping) {
@@ -394,6 +435,14 @@
EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
}
+TEST_P(BinderRpc, MultipleSessions) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*sessions*/);
+ for (auto session : proc.proc.sessions) {
+ ASSERT_NE(nullptr, session.root);
+ EXPECT_EQ(OK, session.root->pingBinder());
+ }
+}
+
TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
auto proc = createRpcTestSocketServerProcess(1);
Parcel data;
@@ -532,7 +581,7 @@
// These are behavioral differences form regular binder, where certain usecases
// aren't supported.
-TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
auto proc1 = createRpcTestSocketServerProcess(1);
auto proc2 = createRpcTestSocketServerProcess(1);
@@ -541,6 +590,15 @@
proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
}
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*sessions*/);
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder)
+ .transactionError());
+}
+
TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
auto proc = createRpcTestSocketServerProcess(1);
@@ -740,7 +798,7 @@
threads.push_back(std::thread([&] {
for (size_t j = 0; j < kNumCalls; j++) {
sp<IBinder> out;
- proc.rootIface->repeatBinder(proc.rootBinder, &out);
+ EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &out));
EXPECT_EQ(proc.rootBinder, out);
}
}));
@@ -749,6 +807,28 @@
for (auto& t : threads) t.join();
}
+TEST_P(BinderRpc, OnewayStressTest) {
+ constexpr size_t kNumClientThreads = 10;
+ constexpr size_t kNumServerThreads = 10;
+ constexpr size_t kNumCalls = 100;
+
+ auto proc = createRpcTestSocketServerProcess(kNumServerThreads);
+
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < kNumClientThreads; i++) {
+ threads.push_back(std::thread([&] {
+ for (size_t j = 0; j < kNumCalls; j++) {
+ EXPECT_OK(proc.rootIface->sendString("a"));
+ }
+
+ // check threads are not stuck
+ EXPECT_OK(proc.rootIface->sleepMs(250));
+ }));
+ }
+
+ for (auto& t : threads) t.join();
+}
+
TEST_P(BinderRpc, OnewayCallDoesNotWait) {
constexpr size_t kReallyLongTimeMs = 100;
constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
@@ -803,7 +883,7 @@
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
<< "Do death cleanup: " << doDeathCleanup;
- proc.proc.expectInvalid = true;
+ proc.expectInvalid = true;
}
}
@@ -852,14 +932,44 @@
}
INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
- ::testing::Values(SocketType::UNIX
+ ::testing::ValuesIn({
+ SocketType::UNIX,
+// TODO(b/185269356): working on host
#ifdef __BIONIC__
- ,
- SocketType::VSOCK
-#endif // __BIONIC__
- ),
+ SocketType::VSOCK,
+#endif
+ SocketType::INET,
+ }),
PrintSocketType);
+class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {};
+
+TEST_P(BinderRpcServerRootObject, WeakRootObject) {
+ using SetFn = std::function<void(RpcServer*, sp<IBinder>)>;
+ auto setRootObject = [](bool isStrong) -> SetFn {
+ return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak);
+ };
+
+ auto server = RpcServer::make();
+ auto [isStrong1, isStrong2] = GetParam();
+ auto binder1 = sp<BBinder>::make();
+ IBinder* binderRaw1 = binder1.get();
+ setRootObject(isStrong1)(server.get(), binder1);
+ EXPECT_EQ(binderRaw1, server->getRootObject());
+ binder1.clear();
+ EXPECT_EQ((isStrong1 ? binderRaw1 : nullptr), server->getRootObject());
+
+ auto binder2 = sp<BBinder>::make();
+ IBinder* binderRaw2 = binder2.get();
+ setRootObject(isStrong2)(server.get(), binder2);
+ EXPECT_EQ(binderRaw2, server->getRootObject());
+ binder2.clear();
+ EXPECT_EQ((isStrong2 ? binderRaw2 : nullptr), server->getRootObject());
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
+ ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
} // namespace android
int main(int argc, char** argv) {
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index cb309bd..2ce13df 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -192,6 +192,8 @@
EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
android::defaultServiceManager()->addService(String16("."), vintfServer)) << instance8;
EXPECT_FALSE(android::defaultServiceManager()->isDeclared(instance)) << instance8;
+ EXPECT_EQ(std::nullopt, android::defaultServiceManager()->updatableViaApex(instance))
+ << instance8;
}
}
diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
similarity index 100%
rename from libs/binder/parcel_fuzzer/Android.bp
rename to libs/binder/tests/parcel_fuzzer/Android.bp
diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.cpp
rename to libs/binder/tests/parcel_fuzzer/binder.cpp
diff --git a/libs/binder/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.h
rename to libs/binder/tests/parcel_fuzzer/binder.h
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder_ndk.cpp
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
similarity index 97%
rename from libs/binder/parcel_fuzzer/binder_ndk.h
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.h
index e69d9c1..cf24ab9 100644
--- a/libs/binder/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -23,7 +23,7 @@
// libbinder_ndk doesn't export this header which breaks down its API for NDK
// and APEX users, but we need access to it to fuzz.
-#include "../ndk/parcel_internal.h"
+#include "../../ndk/parcel_internal.h"
class NdkParcelAdapter {
public:
diff --git a/libs/binder/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.cpp
rename to libs/binder/tests/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/parcel_fuzzer/hwbinder.h b/libs/binder/tests/parcel_fuzzer/hwbinder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.h
rename to libs/binder/tests/parcel_fuzzer/hwbinder.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
rename to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
rename to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
similarity index 95%
rename from libs/binder/parcel_fuzzer/main.cpp
rename to libs/binder/tests/parcel_fuzzer/main.cpp
index 332e2ad..a47b753 100644
--- a/libs/binder/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -23,7 +23,7 @@
#include <iostream>
#include <android-base/logging.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <fuzzbinder/random_parcel.h>
#include <fuzzer/FuzzedDataProvider.h>
@@ -33,7 +33,7 @@
#include <sys/time.h>
using android::fillRandomParcel;
-using android::RpcConnection;
+using android::RpcSession;
using android::sp;
void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
@@ -61,9 +61,9 @@
P p;
if constexpr (std::is_same_v<P, android::Parcel>) {
if (provider.ConsumeBool()) {
- auto connection = sp<RpcConnection>::make();
- CHECK(connection->addNullDebuggingClient());
- p.markForRpc(connection);
+ auto session = sp<RpcSession>::make();
+ CHECK(session->addNullDebuggingClient());
+ p.markForRpc(session);
fillRandomParcelData(&p, std::move(provider));
} else {
fillRandomParcel(&p, std::move(provider));
diff --git a/libs/binder/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/parcel_fuzzer.h
rename to libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/random_fd.cpp
rename to libs/binder/tests/parcel_fuzzer/random_fd.cpp
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/random_parcel.cpp
rename to libs/binder/tests/parcel_fuzzer/random_parcel.cpp
diff --git a/libs/binder/parcel_fuzzer/util.cpp b/libs/binder/tests/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.cpp
rename to libs/binder/tests/parcel_fuzzer/util.cpp
diff --git a/libs/binder/parcel_fuzzer/util.h b/libs/binder/tests/parcel_fuzzer/util.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.h
rename to libs/binder/tests/parcel_fuzzer/util.h
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
new file mode 100644
index 0000000..1c75306
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -0,0 +1,40 @@
+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_fuzz {
+ name: "binder_rpc_fuzzer",
+ host_supported: true,
+
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
+
+ srcs: [
+ "main.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+
+ target: {
+ android: {
+ shared_libs: [
+ "libbinder",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder",
+ ],
+ },
+ },
+}
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
new file mode 100644
index 0000000..3603ebe
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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/unique_fd.h>
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+#include <sys/resource.h>
+#include <sys/un.h>
+
+namespace android {
+
+static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") +
+ "/binderRpcFuzzerSocket_" + std::to_string(getpid());
+
+size_t getHardMemoryLimit() {
+ struct rlimit limit;
+ CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
+ return limit.rlim_max;
+}
+
+void setMemoryLimit(size_t cur, size_t max) {
+ const struct rlimit kLimit = {
+ .rlim_cur = cur,
+ .rlim_max = max,
+ };
+ CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
+}
+
+class SomeBinder : public BBinder {
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {
+ (void)flags;
+
+ if ((code & 1) == 0) {
+ sp<IBinder> binder;
+ (void)data.readStrongBinder(&binder);
+ if (binder != nullptr) {
+ (void)binder->pingBinder();
+ }
+ }
+ if ((code & 2) == 0) {
+ (void)data.readInt32();
+ }
+ if ((code & 4) == 0) {
+ (void)reply->writeStrongBinder(sp<BBinder>::make());
+ }
+
+ return OK;
+ }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size > 50000) return 0;
+
+ unlink(kSock.c_str());
+
+ sp<RpcServer> server = RpcServer::make();
+ server->setRootObject(sp<SomeBinder>::make());
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ CHECK(server->setupUnixDomainServer(kSock.c_str()));
+
+ static constexpr size_t kMemLimit = 1llu * 1024 * 1024 * 1024;
+ size_t hardLimit = getHardMemoryLimit();
+ setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
+
+ std::thread serverThread([=] { (void)server->acceptOne(); });
+
+ sockaddr_un addr{
+ .sun_family = AF_UNIX,
+ };
+ CHECK_LT(kSock.size(), sizeof(addr.sun_path));
+ memcpy(&addr.sun_path, kSock.c_str(), kSock.size());
+
+ base::unique_fd clientFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ CHECK_NE(clientFd.get(), -1);
+ CHECK_EQ(0,
+ TEMP_FAILURE_RETRY(
+ connect(clientFd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))))
+ << strerror(errno);
+
+ serverThread.join();
+
+ // TODO(b/182938024): fuzz multiple sessions, instead of just one
+
+#if 0
+ // make fuzzer more productive locally by forcing it to create a new session
+ int32_t id = -1;
+ CHECK(base::WriteFully(clientFd, &id, sizeof(id)));
+#endif
+
+ CHECK(base::WriteFully(clientFd, data, size));
+
+ clientFd.reset();
+
+ // TODO(b/185167543): better way to force a server to shutdown
+ while (!server->listSessions().empty() && server->numUninitializedSessions()) {
+ usleep(1);
+ }
+
+ setMemoryLimit(hardLimit, hardLimit);
+
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
similarity index 100%
rename from libs/binder/tests/fuzzers/Android.bp
rename to libs/binder/tests/unit_fuzzers/Android.bp
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/TextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
similarity index 100%
rename from libs/binder/tests/fuzzers/commonFuzzHelpers.h
rename to libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
diff --git a/libs/binder/vm_sockets.h b/libs/binder/vm_sockets.h
new file mode 100644
index 0000000..7d9732b
--- /dev/null
+++ b/libs/binder/vm_sockets.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#pragma once
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#else
+
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY (-1U)
+#define VMADDR_PORT_ANY (-1U)
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_LOCAL 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION (-1U)
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF))
+struct sockaddr_vm {
+ sa_family_t svm_family;
+ // NOLINTNEXTLINE(google-runtime-int)
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ // NOLINTNEXTLINE(google-runtime-int)
+ unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) -
+ sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
+
+#endif
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 8a82c2a..29c788b 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -47,6 +47,7 @@
// Native processes to dump on debuggable builds.
static const char* debuggable_native_processes_to_dump[] = {
+ "/system/bin/keystore2",
"/system/bin/vold",
NULL,
};
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 4ecbe53..761e45c 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -73,4 +73,9 @@
return out;
}
+std::optional<String16> ServiceManager::updatableViaApex(const String16& name) {
+ (void)name;
+ return std::nullopt;
+}
+
} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index 4ef47fb..e26c21b 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -19,6 +19,7 @@
#include <binder/IServiceManager.h>
#include <map>
+#include <optional>
namespace android {
@@ -48,6 +49,8 @@
Vector<String16> getDeclaredInstances(const String16& iface) override;
+ std::optional<String16> updatableViaApex(const String16& name) override;
+
private:
std::map<String16, sp<IBinder>> mNameToService;
};
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index dbc1a7e..a2868c6 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -186,6 +186,7 @@
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
int32_t format) {
std::unique_lock _lock{mMutex};
+ BQA_LOGV("update width=%d height=%d format=%d", width, height, format);
if (mFormat != format) {
mFormat = format;
mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
@@ -212,7 +213,8 @@
// 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.
if (mLastBufferInfo.hasBuffer) {
- setMatrix(&t, mLastBufferInfo);
+ t.setDestinationFrame(mSurfaceControl,
+ Rect(0, 0, newSize.getWidth(), newSize.getHeight()));
}
applyTransaction = true;
}
@@ -315,6 +317,11 @@
std::unique_lock _lock{mMutex};
BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
+ if (mSurfaceControl != nullptr) {
+ mTransformHint = mSurfaceControl->getTransformHint();
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+ }
+
auto it = mSubmitted.find(graphicBufferId);
if (it == mSubmitted.end()) {
BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
@@ -397,10 +404,11 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
+ Rect crop = computeCrop(bufferItem);
mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
- bufferItem.mScalingMode);
+ bufferItem.mScalingMode, crop);
auto releaseBufferCallback =
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
@@ -414,8 +422,8 @@
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
- setMatrix(t, mLastBufferInfo);
- t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+ t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+ t->setBufferCrop(mSurfaceControl, crop);
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
if (!bufferItem.mIsAutoTimestamp) {
@@ -541,17 +549,6 @@
return mSize != bufferSize;
}
-void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
- const BufferInfo& bufferInfo) {
- uint32_t bufWidth = bufferInfo.width;
- uint32_t bufHeight = bufferInfo.height;
-
- float dsdx = mSize.width / static_cast<float>(bufWidth);
- float dsdy = mSize.height / static_cast<float>(bufHeight);
-
- t->setMatrix(mSurfaceControl, dsdx, 0, 0, dsdy);
-}
-
void BLASTBufferQueue::setTransactionCompleteCallback(
uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
std::lock_guard _lock{mMutex};
@@ -604,6 +601,14 @@
status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
return mBbq->setFrameTimelineInfo(frameTimelineInfo);
}
+ protected:
+ uint32_t getTransformHint() const override {
+ if (mStickyTransform == 0 && !transformToDisplayInverse()) {
+ return mBbq->getLastTransformHint();
+ } else {
+ return 0;
+ }
+ }
};
// TODO: Can we coalesce this with frame updates? Need to confirm
@@ -662,12 +667,12 @@
void run() {
std::unique_lock<std::mutex> lock(mMutex);
while (!mDone) {
- mCv.wait(lock);
while (!mRunnables.empty()) {
std::function<void()> runnable = mRunnables.front();
mRunnables.pop_front();
runnable();
}
+ mCv.wait(lock);
}
}
@@ -773,4 +778,12 @@
return convertedFormat;
}
+uint32_t BLASTBufferQueue::getLastTransformHint() const {
+ if (mSurfaceControl != nullptr) {
+ return mSurfaceControl->getTransformHint();
+ } else {
+ return 0;
+ }
+}
+
} // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a7cf39a..df308d8 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1621,6 +1621,26 @@
return NO_ERROR;
}
+status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+ Rect* outRect, uint32_t* outTransform) {
+ ATRACE_CALL();
+ BQ_LOGV("getLastQueuedBuffer");
+
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
+ *outBuffer = nullptr;
+ *outFence = Fence::NO_FENCE;
+ return NO_ERROR;
+ }
+
+ *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer;
+ *outFence = mLastQueueBufferFence;
+ *outRect = mLastQueuedCrop;
+ *outTransform = mLastQueuedTransform;
+
+ return NO_ERROR;
+}
+
void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
addAndGetFrameTimestamps(nullptr, outDelta);
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index c1f9b85..797069c 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -81,6 +81,7 @@
QUEUE_BUFFERS,
CANCEL_BUFFERS,
QUERY_MULTIPLE,
+ GET_LAST_QUEUED_BUFFER2,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -646,6 +647,56 @@
return result;
}
+ virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+ Rect* outRect, uint32_t* outTransform) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER2, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to transact: %d", result);
+ return result;
+ }
+ status_t remoteError = NO_ERROR;
+ result = reply.readInt32(&remoteError);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to read status: %d", result);
+ return result;
+ }
+ if (remoteError != NO_ERROR) {
+ return remoteError;
+ }
+ bool hasBuffer = false;
+ result = reply.readBool(&hasBuffer);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+ return result;
+ }
+ sp<GraphicBuffer> buffer;
+ if (hasBuffer) {
+ buffer = new GraphicBuffer();
+ result = reply.read(*buffer);
+ if (result == NO_ERROR) {
+ result = reply.read(*outRect);
+ }
+ if (result == NO_ERROR) {
+ result = reply.readUint32(outTransform);
+ }
+ }
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+ return result;
+ }
+ sp<Fence> fence(new Fence);
+ result = reply.read(*fence);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
+ return result;
+ }
+ *outBuffer = buffer;
+ *outFence = fence;
+ return result;
+ }
+
virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(
@@ -870,6 +921,11 @@
outBuffer, outFence, outTransformMatrix);
}
+ status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect,
+ uint32_t* outTransform) override {
+ return mBase->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
+ }
+
void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
return mBase->getFrameTimestamps(outDelta);
}
@@ -1362,6 +1418,45 @@
}
return NO_ERROR;
}
+ case GET_LAST_QUEUED_BUFFER2: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ sp<GraphicBuffer> buffer(nullptr);
+ sp<Fence> fence(Fence::NO_FENCE);
+ Rect crop;
+ uint32_t transform;
+ status_t result = getLastQueuedBuffer(&buffer, &fence, &crop, &transform);
+ reply->writeInt32(result);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ if (!buffer.get()) {
+ reply->writeBool(false);
+ } else {
+ reply->writeBool(true);
+ result = reply->write(*buffer);
+ if (result == NO_ERROR) {
+ result = reply->write(crop);
+ }
+ if (result == NO_ERROR) {
+ result = reply->writeUint32(transform);
+ }
+ }
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
+ return result;
+ }
+ if (fence == nullptr) {
+ ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE");
+ fence = Fence::NO_FENCE;
+ }
+ result = reply->write(*fence);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to write fence: %d", result);
+ return result;
+ }
+ return NO_ERROR;
+ }
+
case GET_FRAME_TIMESTAMPS: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
FrameEventHistoryDelta frameTimestamps;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 454aa9e..53721cf 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -99,7 +99,7 @@
SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
for (const auto& [listener, callbackIds] : listenerCallbacks) {
SAFE_PARCEL(data.writeStrongBinder, listener);
- SAFE_PARCEL(data.writeInt64Vector, callbackIds);
+ SAFE_PARCEL(data.writeParcelableVector, callbackIds);
}
SAFE_PARCEL(data.writeUint64, transactionId);
@@ -497,6 +497,28 @@
return result;
}
+ status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeInt32, atomId);
+
+ status_t err = remote()->transact(BnSurfaceComposer::ON_PULL_ATOM, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("onPullAtom failed to transact: %d", err);
+ return err;
+ }
+
+ int32_t size = 0;
+ SAFE_PARCEL(reply.readInt32, &size);
+ const void* dataPtr = reply.readInplace(size);
+ if (dataPtr == nullptr) {
+ return UNEXPECTED_NULL;
+ }
+ pulledData->assign((const char*)dataPtr, size);
+ SAFE_PARCEL(reply.readBool, success);
+ return NO_ERROR;
+ }
+
status_t enableVSyncInjections(bool enable) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1246,7 +1268,7 @@
for (int32_t i = 0; i < listenersSize; i++) {
SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
std::vector<CallbackId> callbackIds;
- SAFE_PARCEL(data.readInt64Vector, &callbackIds);
+ SAFE_PARCEL(data.readParcelableVector, &callbackIds);
listenerCallbacks.emplace_back(tmpBinder, callbackIds);
}
@@ -2021,6 +2043,19 @@
}
return overrideHdrTypes(display, hdrTypesVector);
}
+ case ON_PULL_ATOM: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t atomId = 0;
+ SAFE_PARCEL(data.readInt32, &atomId);
+
+ std::string pulledData;
+ bool success;
+ status_t err = onPullAtom(atomId, &pulledData, &success);
+ SAFE_PARCEL(reply->writeByteArray, pulledData.size(),
+ reinterpret_cast<const uint8_t*>(pulledData.data()));
+ SAFE_PARCEL(reply->writeBool, success);
+ return err;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index b42793b..f74f91e 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -152,7 +152,7 @@
}
status_t TransactionStats::writeToParcel(Parcel* output) const {
- status_t err = output->writeInt64Vector(callbackIds);
+ status_t err = output->writeParcelableVector(callbackIds);
if (err != NO_ERROR) {
return err;
}
@@ -176,7 +176,7 @@
}
status_t TransactionStats::readFromParcel(const Parcel* input) {
- status_t err = input->readInt64Vector(&callbackIds);
+ status_t err = input->readParcelableVector(&callbackIds);
if (err != NO_ERROR) {
return err;
}
@@ -227,8 +227,9 @@
return NO_ERROR;
}
-ListenerStats ListenerStats::createEmpty(const sp<IBinder>& listener,
- const std::unordered_set<CallbackId>& callbackIds) {
+ListenerStats ListenerStats::createEmpty(
+ const sp<IBinder>& listener,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
ListenerStats listenerStats;
listenerStats.listener = listener;
listenerStats.transactionStats.emplace_back(callbackIds);
@@ -278,4 +279,28 @@
}
}
+ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
+ std::vector<CallbackId> filteredCallbackIds;
+ for (const auto& callbackId : callbackIds) {
+ if (callbackId.type == type) {
+ filteredCallbackIds.push_back(callbackId);
+ }
+ }
+ return ListenerCallbacks(transactionCompletedListener, filteredCallbackIds);
+}
+
+status_t CallbackId::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeInt64, id);
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
+ return NO_ERROR;
+}
+
+status_t CallbackId::readFromParcel(const Parcel* input) {
+ SAFE_PARCEL(input->readInt64, &id);
+ int32_t typeAsInt;
+ SAFE_PARCEL(input->readInt32, &typeAsInt);
+ type = static_cast<CallbackId::Type>(typeAsInt);
+ return NO_ERROR;
+}
+
}; // namespace android
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index e707684..0827bbe 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -119,9 +119,11 @@
info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
if (info.mStretchEffect.hasEffect()) {
const auto& se = info.mStretchEffect;
- StringAppendF(&result, " StretchEffect area=[%f, %f, %f, %f] vec=(%f, %f) maxAmount=%f\n",
- se.area.left, se.area.top, se.area.right, se.area.bottom, se.vectorX,
- se.vectorY, se.maxAmount);
+ StringAppendF(&result,
+ " StretchEffect width = %f, height = %f vec=(%f, %f) "
+ "maxAmount=(%f, %f)\n",
+ se.width, se.height,
+ se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY);
}
StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 55ed7fe..e65c721 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -64,6 +64,8 @@
fixedTransformHint(ui::Transform::ROT_INVALID),
frameNumber(0),
autoRefresh(false),
+ bufferCrop(Rect::INVALID_RECT),
+ destinationFrame(Rect::INVALID_RECT),
releaseBufferListener(nullptr) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
@@ -98,7 +100,6 @@
SAFE_PARCEL(output.write, transparentRegion);
SAFE_PARCEL(output.writeUint32, transform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
- SAFE_PARCEL(output.write, crop);
SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
if (buffer) {
@@ -140,7 +141,7 @@
for (auto listener : listeners) {
SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener);
- SAFE_PARCEL(output.writeInt64Vector, listener.callbackIds);
+ SAFE_PARCEL(output.writeParcelableVector, listener.callbackIds);
}
SAFE_PARCEL(output.writeFloat, shadowRadius);
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
@@ -167,6 +168,8 @@
}
SAFE_PARCEL(output.write, stretchEffect);
+ SAFE_PARCEL(output.write, bufferCrop);
+ SAFE_PARCEL(output.write, destinationFrame);
return NO_ERROR;
}
@@ -209,7 +212,6 @@
SAFE_PARCEL(input.read, transparentRegion);
SAFE_PARCEL(input.readUint32, &transform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
- SAFE_PARCEL(input.read, crop);
SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
bool tmpBool = false;
@@ -258,7 +260,7 @@
sp<IBinder> listener;
std::vector<CallbackId> callbackIds;
SAFE_PARCEL(input.readNullableStrongBinder, &listener);
- SAFE_PARCEL(input.readInt64Vector, &callbackIds);
+ SAFE_PARCEL(input.readParcelableVector, &callbackIds);
listeners.emplace_back(listener, callbackIds);
}
SAFE_PARCEL(input.readFloat, &shadowRadius);
@@ -296,6 +298,8 @@
}
SAFE_PARCEL(input.read, stretchEffect);
+ SAFE_PARCEL(input.read, bufferCrop);
+ SAFE_PARCEL(input.read, destinationFrame);
return NO_ERROR;
}
@@ -539,6 +543,14 @@
what |= eStretchChanged;
stretchEffect = other.stretchEffect;
}
+ if (other.what & eBufferCropChanged) {
+ what |= eBufferCropChanged;
+ bufferCrop = other.bufferCrop;
+ }
+ if (other.what & eDestinationFrameChanged) {
+ what |= eDestinationFrameChanged;
+ destinationFrame = other.destinationFrame;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2fc9d47..d27d1ec 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1288,7 +1288,7 @@
mUserHeight ? mUserHeight : mDefaultHeight);
return NO_ERROR;
case NATIVE_WINDOW_TRANSFORM_HINT:
- *value = static_cast<int>(mTransformHint);
+ *value = static_cast<int>(getTransformHint());
return NO_ERROR;
case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
status_t err = NO_ERROR;
@@ -1492,6 +1492,9 @@
case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
res = dispatchGetLastQueuedBuffer(args);
break;
+ case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2:
+ res = dispatchGetLastQueuedBuffer2(args);
+ break;
case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO:
res = dispatchSetFrameTimelineInfo(args);
break;
@@ -1805,6 +1808,39 @@
return result;
}
+int Surface::dispatchGetLastQueuedBuffer2(va_list args) {
+ AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
+ int* fence = va_arg(args, int*);
+ ARect* crop = va_arg(args, ARect*);
+ uint32_t* transform = va_arg(args, uint32_t*);
+ sp<GraphicBuffer> graphicBuffer;
+ sp<Fence> spFence;
+
+ Rect r;
+ int result =
+ mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, &r, transform);
+
+ if (graphicBuffer != nullptr) {
+ *buffer = graphicBuffer->toAHardwareBuffer();
+ AHardwareBuffer_acquire(*buffer);
+
+ // Avoid setting crop* unless buffer is valid (matches IGBP behavior)
+ crop->left = r.left;
+ crop->top = r.top;
+ crop->right = r.right;
+ crop->bottom = r.bottom;
+ } else {
+ *buffer = nullptr;
+ }
+
+ if (spFence != nullptr) {
+ *fence = spFence->dup();
+ } else {
+ *fence = -1;
+ }
+ return result;
+}
+
int Surface::dispatchSetFrameTimelineInfo(va_list args) {
ATRACE_CALL();
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
@@ -1822,7 +1858,7 @@
return getExtraBufferCount(extraBuffers);
}
-bool Surface::transformToDisplayInverse() {
+bool Surface::transformToDisplayInverse() const {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 6d198a1..aa93808 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -139,7 +139,7 @@
// 0 is an invalid callback id
TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
-CallbackId TransactionCompletedListener::getNextIdLocked() {
+int64_t TransactionCompletedListener::getNextIdLocked() {
return mCallbackIdCounter++;
}
@@ -163,13 +163,13 @@
CallbackId TransactionCompletedListener::addCallbackFunction(
const TransactionCompletedCallback& callbackFunction,
const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
- surfaceControls) {
+ surfaceControls,
+ CallbackId::Type callbackType) {
std::lock_guard<std::mutex> lock(mMutex);
startListeningLocked();
- CallbackId callbackId = getNextIdLocked();
+ CallbackId callbackId(getNextIdLocked(), callbackType);
mCallbacks[callbackId].callbackFunction = callbackFunction;
-
auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
for (const auto& surfaceControl : surfaceControls) {
@@ -228,7 +228,7 @@
void TransactionCompletedListener::addSurfaceControlToCallbacks(
const sp<SurfaceControl>& surfaceControl,
- const std::unordered_set<CallbackId>& callbackIds) {
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
std::lock_guard<std::mutex> lock(mMutex);
for (auto callbackId : callbackIds) {
@@ -240,7 +240,7 @@
}
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
- std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+ std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> surfaceListeners;
{
@@ -267,7 +267,36 @@
}
}
for (const auto& transactionStats : listenerStats.transactionStats) {
+ // handle on commit callbacks
for (auto callbackId : transactionStats.callbackIds) {
+ if (callbackId.type != CallbackId::Type::ON_COMMIT) {
+ continue;
+ }
+ auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
+ if (!callbackFunction) {
+ ALOGE("cannot call null callback function, skipping");
+ continue;
+ }
+ std::vector<SurfaceControlStats> surfaceControlStats;
+ for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ surfaceControlStats
+ .emplace_back(callbacksMap[callbackId]
+ .surfaceControls[surfaceStats.surfaceControl],
+ transactionStats.latchTime, surfaceStats.acquireTime,
+ transactionStats.presentFence,
+ surfaceStats.previousReleaseFence, surfaceStats.transformHint,
+ surfaceStats.eventStats);
+ }
+
+ callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+ surfaceControlStats);
+ }
+
+ // handle on complete callbacks
+ for (auto callbackId : transactionStats.callbackIds) {
+ if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+ continue;
+ }
auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
if (!callbackFunction) {
ALOGE("cannot call null callback function, skipping");
@@ -542,7 +571,9 @@
return BAD_VALUE;
}
for (size_t j = 0; j < numCallbackIds; j++) {
- listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+ CallbackId id;
+ parcel->readParcelable(&id);
+ listenerCallbacks[listener].callbackIds.insert(id);
}
size_t numSurfaces = parcel->readUint32();
if (numSurfaces > parcel->dataSize()) {
@@ -628,7 +659,7 @@
parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
for (auto callbackId : callbackInfo.callbackIds) {
- parcel->writeInt64(callbackId);
+ parcel->writeParcelable(callbackId);
}
parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
for (auto surfaceControl : callbackInfo.surfaceControls) {
@@ -1389,9 +1420,9 @@
return *this;
}
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
- TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext,
+ CallbackId::Type callbackType) {
auto listener = TransactionCompletedListener::getInstance();
auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
@@ -1399,13 +1430,26 @@
const auto& surfaceControls =
mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
- CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
+ CallbackId callbackId =
+ listener->addCallbackFunction(callbackWithContext, surfaceControls, callbackType);
mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
callbackId);
return *this;
}
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+ return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMPLETE);
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCommittedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+ return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMMIT);
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
const sp<SurfaceControl>& sc) {
layer_state_t* s = getLayerState(sc);
@@ -1604,8 +1648,7 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect(
- const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX,
- float vecY, float maxAmount) {
+ const sp<SurfaceControl>& sc, const StretchEffect& stretchEffect) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1613,10 +1656,37 @@
}
s->what |= layer_state_t::eStretchChanged;
- s->stretchEffect = StretchEffect{.area = {left, top, right, bottom},
- .vectorX = vecX,
- .vectorY = vecY,
- .maxAmount = maxAmount};
+ s->stretchEffect = stretchEffect;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop(
+ const sp<SurfaceControl>& sc, const Rect& bufferCrop) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBufferCropChanged;
+ s->bufferCrop = bufferCrop;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDestinationFrame(
+ const sp<SurfaceControl>& sc, const Rect& destinationFrame) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eDestinationFrameChanged;
+ s->destinationFrame = destinationFrame;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -1796,7 +1866,8 @@
}
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
- *outSurface = new SurfaceControl(this, handle, gbp, id, w, h, format, transformHint);
+ *outSurface =
+ new SurfaceControl(this, handle, gbp, id, w, h, format, transformHint, flags);
}
}
return err;
@@ -1949,6 +2020,11 @@
return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes);
}
+status_t SurfaceComposerClient::onPullAtom(const int32_t atomId, std::string* outData,
+ bool* success) {
+ return ComposerService::getComposerService()->onPullAtom(atomId, outData, success);
+}
+
status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 37750fa..d7c07b9 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -166,7 +166,7 @@
Mutex::Autolock _l(mLock);
mWidth = width; mHeight = height;
if (mBbq) {
- mBbq->update(this, width, height, mFormat);
+ mBbq->update(mBbqChild, width, height, mFormat);
}
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 8a23223..c4ca399 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -103,6 +103,8 @@
void setSidebandStream(const sp<NativeHandle>& stream);
+ uint32_t getLastTransformHint() const;
+
virtual ~BLASTBufferQueue();
private:
@@ -151,14 +153,20 @@
// we get the next buffer. This will support scenarios where the layer can change sizes
// and the buffer will scale to fit the new size.
uint32_t scalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+ Rect crop;
void update(bool hasBuffer, uint32_t width, uint32_t height, uint32_t transform,
- uint32_t scalingMode) {
+ uint32_t scalingMode, const Rect& crop) {
this->hasBuffer = hasBuffer;
this->width = width;
this->height = height;
this->transform = transform;
this->scalingMode = scalingMode;
+ if (!crop.isEmpty()) {
+ this->crop = crop;
+ } else {
+ this->crop = Rect(width, height);
+ }
}
};
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index a7f7d1d..0ad3075 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -186,6 +186,10 @@
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) override;
+ // See IGraphicBufferProducer::getLastQueuedBuffer
+ virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+ Rect* outRect, uint32_t* outTransform) override;
+
// See IGraphicBufferProducer::getFrameTimestamps
virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index c3b9262..98df834 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -640,6 +640,22 @@
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
+ // Returns the last queued buffer along with a fence which must signal
+ // before the contents of the buffer are read. If there are no buffers in
+ // the queue, outBuffer will be populated with nullptr and outFence will be
+ // populated with Fence::NO_FENCE
+ //
+ // outRect & outTransform are not modified if outBuffer is null.
+ //
+ // Returns NO_ERROR or the status of the Binder transaction
+ virtual status_t getLastQueuedBuffer([[maybe_unused]] sp<GraphicBuffer>* outBuffer,
+ [[maybe_unused]] sp<Fence>* outFence,
+ [[maybe_unused]] Rect* outRect,
+ [[maybe_unused]] uint32_t* outTransform) {
+ // Too many things implement IGraphicBufferProducer...
+ return UNKNOWN_TRANSACTION;
+ }
+
// Gets the frame events that haven't already been retrieved.
virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index ccd6d4e..cb04689 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -275,6 +275,12 @@
virtual status_t overrideHdrTypes(const sp<IBinder>& display,
const std::vector<ui::Hdr>& hdrTypes) = 0;
+ /* Pulls surfaceflinger atoms global stats and layer stats to pipe to statsd.
+ *
+ * Requires the calling uid be from system server.
+ */
+ virtual status_t onPullAtom(const int32_t atomId, std::string* outData, bool* success) = 0;
+
virtual status_t enableVSyncInjections(bool enable) = 0;
virtual status_t injectVSync(nsecs_t when) = 0;
@@ -600,6 +606,7 @@
OVERRIDE_HDR_TYPES,
ADD_HDR_LAYER_INFO_LISTENER,
REMOVE_HDR_LAYER_INFO_LISTENER,
+ ON_PULL_ATOM,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 098760e..2d71194 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -36,7 +36,22 @@
class ITransactionCompletedListener;
class ListenerCallbacks;
-using CallbackId = int64_t;
+class CallbackId : public Parcelable {
+public:
+ int64_t id;
+ enum class Type : int32_t { ON_COMPLETE, ON_COMMIT } type;
+
+ CallbackId() {}
+ CallbackId(int64_t id, Type type) : id(id), type(type) {}
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ bool operator==(const CallbackId& rhs) const { return id == rhs.id && type == rhs.type; }
+};
+
+struct CallbackIdHash {
+ std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
+};
class FrameEventHistoryStats : public Parcelable {
public:
@@ -112,7 +127,7 @@
TransactionStats() = default;
TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
- TransactionStats(const std::unordered_set<CallbackId>& ids)
+ TransactionStats(const std::unordered_set<CallbackId, CallbackIdHash>& ids)
: callbackIds(ids.begin(), ids.end()) {}
TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
const std::vector<SurfaceStats>& surfaces)
@@ -129,8 +144,9 @@
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
- static ListenerStats createEmpty(const sp<IBinder>& listener,
- const std::unordered_set<CallbackId>& callbackIds);
+ static ListenerStats createEmpty(
+ const sp<IBinder>& listener,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
sp<IBinder> listener;
std::vector<TransactionStats> transactionStats;
@@ -156,7 +172,8 @@
class ListenerCallbacks {
public:
- ListenerCallbacks(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbacks)
+ ListenerCallbacks(const sp<IBinder>& listener,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbacks)
: transactionCompletedListener(listener),
callbackIds(callbacks.begin(), callbacks.end()) {}
@@ -170,9 +187,12 @@
if (callbackIds.empty()) {
return rhs.callbackIds.empty();
}
- return callbackIds.front() == rhs.callbackIds.front();
+ return callbackIds.front().id == rhs.callbackIds.front().id;
}
+ // Returns a new ListenerCallbacks filtered by type
+ ListenerCallbacks filter(CallbackId::Type type) const;
+
sp<IBinder> transactionCompletedListener;
std::vector<CallbackId> callbackIds;
};
@@ -191,7 +211,7 @@
// same members. It is sufficient to just check the first CallbackId in the vectors. If
// they match, they are the same. If they do not match, they are not the same.
std::size_t operator()(const std::vector<CallbackId>& callbackIds) const {
- return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front());
+ return std::hash<int64_t>{}((callbackIds.empty()) ? 0 : callbackIds.front().id);
}
};
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 85ae9cb..ce9716f 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -42,6 +42,10 @@
BufferStuffing = 0x40,
// Jank due to unknown reasons.
Unknown = 0x80,
+ // SF is said to be stuffed if the previous frame ran longer than expected resulting in the case
+ // where the previous frame was presented in the current frame's expected vsync. This pushes the
+ // current frame to the next vsync. The behavior is similar to BufferStuffing.
+ SurfaceFlingerStuffing = 0x100,
};
} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index d2d1e5b..16430b3 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -84,7 +84,8 @@
eLayerStackChanged = 0x00000080,
eReleaseBufferListenerChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
- /* was eDetachChildren, now available 0x00002000, */
+ eLayerCreated = 0x00001000,
+ eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
eColorChanged = 0x00010000,
@@ -103,7 +104,7 @@
eHasListenerCallbacksChanged = 0x20000000,
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
- /* was eFrameChanged, now available 0x1'00000000, */
+ eDestinationFrameChanged = 0x1'00000000,
eCachedBufferChanged = 0x2'00000000,
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
@@ -226,6 +227,9 @@
// Stretch effect to be applied to this layer
StretchEffect stretchEffect;
+ Rect bufferCrop;
+ Rect destinationFrame;
+
// Listens to when the buffer is safe to be released. This is used for blast
// layers only. The callback includes a release fence as well as the graphic
// buffer id to identify the buffer.
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index d22bdaa..48885eb 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -276,9 +276,9 @@
int dispatchAddQueueInterceptor(va_list args);
int dispatchAddQueryInterceptor(va_list args);
int dispatchGetLastQueuedBuffer(va_list args);
+ int dispatchGetLastQueuedBuffer2(va_list args);
int dispatchSetFrameTimelineInfo(va_list args);
int dispatchGetExtraBufferCount(va_list args);
- bool transformToDisplayInverse();
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -490,6 +490,8 @@
// mTransformHint is the transform probably applied to buffers of this
// window. this is only a hint, actual transform may differ.
uint32_t mTransformHint;
+ virtual uint32_t getTransformHint() const { return mTransformHint; }
+ bool transformToDisplayInverse() const;
// mProducerControlledByApp whether this buffer producer is controlled
// by the application
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index fe6a30a..0940e9d 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -336,7 +336,7 @@
struct CallbackInfo {
// All the callbacks that have been requested for a TransactionCompletedListener in the
// Transaction
- std::unordered_set<CallbackId> callbackIds;
+ std::unordered_set<CallbackId, CallbackIdHash> callbackIds;
// All the SurfaceControls that have been modified in this TransactionCompletedListener's
// process that require a callback if there is one or more callbackIds set.
std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
@@ -484,9 +484,15 @@
// Sets information about the priority of the frame.
Transaction& setFrameRateSelectionPriority(const sp<SurfaceControl>& sc, int32_t priority);
+ Transaction& addTransactionCallback(TransactionCompletedCallbackTakesContext callback,
+ void* callbackContext, CallbackId::Type callbackType);
+
Transaction& addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+ Transaction& addTransactionCommittedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
// Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
@@ -532,9 +538,24 @@
// transactions from blocking each other.
Transaction& setApplyToken(const sp<IBinder>& token);
- Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top,
- float right, float bottom, float vecX, float vecY,
- float maxAmount);
+ /**
+ * Provides the stretch effect configured on a container that the
+ * surface is rendered within.
+ * @param sc target surface the stretch should be applied to
+ * @param stretchEffect the corresponding stretch effect to be applied
+ * to the surface. This can be directly on the surface itself or
+ * configured from a parent of the surface in which case the
+ * StretchEffect provided has parameters mapping the position of
+ * the surface within the container that has the stretch configured
+ * on it
+ * @return The transaction being constructed
+ */
+ Transaction& setStretchEffect(const sp<SurfaceControl>& sc,
+ const StretchEffect& stretchEffect);
+
+ Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
+ Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
+ const Rect& destinationFrame);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -567,6 +588,8 @@
static status_t overrideHdrTypes(const sp<IBinder>& display,
const std::vector<ui::Hdr>& hdrTypes);
+ static status_t onPullAtom(const int32_t atomId, std::string* outData, bool* success);
+
static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
const Rect& layerStackRect, const Rect& displayRect);
@@ -619,14 +642,13 @@
class TransactionCompletedListener : public BnTransactionCompletedListener {
TransactionCompletedListener();
- CallbackId getNextIdLocked() REQUIRES(mMutex);
+ int64_t getNextIdLocked() REQUIRES(mMutex);
std::mutex mMutex;
bool mListening GUARDED_BY(mMutex) = false;
- CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
-
+ int64_t mCallbackIdCounter GUARDED_BY(mMutex) = 1;
struct CallbackTranslation {
TransactionCompletedCallback callbackFunction;
std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
@@ -644,7 +666,8 @@
SurfaceStatsCallback callback;
};
- std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+ std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
+ GUARDED_BY(mMutex);
std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
mReleaseBufferCallbacks GUARDED_BY(mMutex);
@@ -660,10 +683,12 @@
CallbackId addCallbackFunction(
const TransactionCompletedCallback& callbackFunction,
const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
- surfaceControls);
+ surfaceControls,
+ CallbackId::Type callbackType);
- void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
- const std::unordered_set<CallbackId>& callbackIds);
+ void addSurfaceControlToCallbacks(
+ const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
/*
* Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
diff --git a/services/surfaceflinger/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
similarity index 89%
rename from services/surfaceflinger/TraceUtils.h
rename to libs/gui/include/gui/TraceUtils.h
index 90a34a5..b9ec14a 100644
--- a/services/surfaceflinger/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// TODO(b/183120308): This file is a copy of f/b/libs/hwui/utils/TraceUtils.h
-// It should be migrated to a common place where both SF and hwui could use it.
-
#pragma once
#include <cutils/trace.h>
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index a44f44f..5a5da97 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -522,16 +522,146 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
-
- Rect bounds;
- bounds.left = finalCropSideLength / 2;
- bounds.top = 0;
- bounds.right = bounds.left + finalCropSideLength;
- bounds.bottom = finalCropSideLength;
-
- ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b, bounds));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
+ {10, 10, (int32_t)bufferSideLength - 10,
+ (int32_t)bufferSideLength - 10}));
ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(0, 0, 0, bounds, /*border*/ 0, /*outsideRegion*/ true));
+ checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
+ /*border*/ 0, /*outsideRegion*/ true));
+}
+
+TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
+ // add black background
+ auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect);
+ ASSERT_NE(nullptr, bg.get());
+ Transaction t;
+ t.setLayerStack(bg, 0)
+ .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setColor(bg, half3{0, 0, 0})
+ .setLayer(bg, 0)
+ .apply();
+
+ Rect windowSize(1000, 1000);
+ Rect bufferSize(windowSize);
+ Rect bufferCrop(200, 200, 700, 700);
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
+ bufferSize.getHeight(), PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ // fill buffer with grey
+ fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);
+
+ // fill crop area with different colors so we can verify the cropped region has been scaled
+ // correctly.
+ fillBuffer(bufData, Rect(200, 200, 450, 450), buf->getStride(), /* rgb */ 255, 0, 0);
+ fillBuffer(bufData, Rect(200, 451, 450, 700), buf->getStride(), /* rgb */ 0, 255, 0);
+ fillBuffer(bufData, Rect(451, 200, 700, 450), buf->getStride(), /* rgb */ 0, 0, 255);
+ fillBuffer(bufData, Rect(451, 451, 700, 700), buf->getStride(), /* rgb */ 255, 0, 0);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
+ bufferCrop /* Rect::INVALID_RECT */,
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+ // Verify cropped region is scaled correctly.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
+ // Verify outside region is black.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)windowSize.getWidth(),
+ (int32_t)windowSize.getHeight()},
+ /*border*/ 0, /*outsideRegion*/ true));
+}
+
+TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
+ // add black background
+ auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect);
+ ASSERT_NE(nullptr, bg.get());
+ Transaction t;
+ t.setLayerStack(bg, 0)
+ .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setColor(bg, half3{0, 0, 0})
+ .setLayer(bg, 0)
+ .apply();
+
+ Rect windowSize(1000, 1000);
+ Rect bufferSize(500, 500);
+ Rect bufferCrop(100, 100, 350, 350);
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
+ bufferSize.getHeight(), PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ // fill buffer with grey
+ fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);
+
+ // fill crop area with different colors so we can verify the cropped region has been scaled
+ // correctly.
+ fillBuffer(bufData, Rect(100, 100, 225, 225), buf->getStride(), /* rgb */ 255, 0, 0);
+ fillBuffer(bufData, Rect(100, 226, 225, 350), buf->getStride(), /* rgb */ 0, 255, 0);
+ fillBuffer(bufData, Rect(226, 100, 350, 225), buf->getStride(), /* rgb */ 0, 0, 255);
+ fillBuffer(bufData, Rect(226, 226, 350, 350), buf->getStride(), /* rgb */ 255, 0, 0);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
+ bufferCrop /* Rect::INVALID_RECT */,
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ // Verify cropped region is scaled correctly.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
+ // Verify outside region is black.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)windowSize.getWidth(),
+ (int32_t)windowSize.getHeight()},
+ /*border*/ 0, /*outsideRegion*/ true));
}
class TestProducerListener : public BnProducerListener {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 751b95a..ea8c295 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -102,14 +102,13 @@
// test flakiness.
mSurfaceControl = mComposerClient->createSurface(
String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
+ SurfaceComposerClient::Transaction().apply(true);
ASSERT_TRUE(mSurfaceControl != nullptr);
ASSERT_TRUE(mSurfaceControl->isValid());
Transaction t;
- ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7fffffff)
- .show(mSurfaceControl)
- .apply());
+ ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7fffffff).show(mSurfaceControl).apply());
mSurface = mSurfaceControl->getSurface();
ASSERT_TRUE(mSurface != nullptr);
@@ -776,6 +775,10 @@
const std::vector<ui::Hdr>& /*hdrTypes*/) override {
return NO_ERROR;
}
+ status_t onPullAtom(const int32_t /*atomId*/, std::string* /*outData*/,
+ bool* /*success*/) override {
+ return NO_ERROR;
+ }
status_t enableVSyncInjections(bool /*enable*/) override {
return NO_ERROR;
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 6f79f4b..a63ec8f 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -49,7 +49,6 @@
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
- "LatencyStatistics.cpp",
"PropertyMap.cpp",
"TouchVideoFrame.cpp",
"VelocityControl.cpp",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 5600eb3..649a140 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -340,7 +340,8 @@
int32_t flags, int32_t edgeFlags, int32_t metaState,
int32_t buttonState, MotionClassification classification,
const ui::Transform& transform, float xPrecision, float yPrecision,
- float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime,
+ float rawXCursorPosition, float rawYCursorPosition,
+ int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
nsecs_t eventTime, size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
@@ -357,6 +358,8 @@
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
mRawYCursorPosition = rawYCursorPosition;
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
mDownTime = downTime;
mPointerProperties.clear();
mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -380,6 +383,8 @@
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
mRawYCursorPosition = other->mRawYCursorPosition;
+ mDisplayWidth = other->mDisplayWidth;
+ mDisplayHeight = other->mDisplayHeight;
mDownTime = other->mDownTime;
mPointerProperties = other->mPointerProperties;
@@ -426,7 +431,7 @@
}
float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
- return getRawPointerCoords(pointerIndex)->getAxisValue(axis);
+ return getHistoricalRawAxisValue(axis, pointerIndex, getHistorySize());
}
float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
@@ -440,7 +445,32 @@
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) {
+ return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ }
+ // 0x7 encapsulates all 3 rotations (see ui::Transform::RotationFlags)
+ static const int ALL_ROTATIONS_MASK = 0x7;
+ uint32_t orientation = (mTransform.getOrientation() & ALL_ROTATIONS_MASK);
+ if (orientation == ui::Transform::ROT_0) {
+ return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ }
+
+ // For compatibility, convert raw coordinates into "oriented screen space". Once app developers
+ // are educated about getRaw, we can consider removing this.
+ vec2 xy = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue();
+ const float unrotatedX = xy.x;
+ if (orientation == ui::Transform::ROT_90) {
+ xy.x = mDisplayHeight - xy.y;
+ xy.y = unrotatedX;
+ } else if (orientation == ui::Transform::ROT_180) {
+ xy.x = mDisplayWidth - xy.x;
+ xy.y = mDisplayHeight - xy.y;
+ } else if (orientation == ui::Transform::ROT_270) {
+ xy.x = xy.y;
+ xy.y = mDisplayWidth - unrotatedX;
+ }
+ static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+ return xy[axis];
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
@@ -449,19 +479,10 @@
return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
}
- float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX();
- float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY();
- vec2 vals = mTransform.transform(rawX, rawY);
-
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- return vals.x;
- case AMOTION_EVENT_AXIS_Y:
- return vals.y;
- }
-
- // This should never happen
- return 0;
+ vec2 vals = mTransform.transform(
+ getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue());
+ static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+ return vals[axis];
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -548,6 +569,24 @@
}
}
+void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
+ // Determine how the origin is transformed by the matrix so that we
+ // can transform orientation vectors.
+ vec2 origin = transformPoint(matrix, 0, 0);
+
+ // Apply the transformation to all samples.
+ size_t numSamples = mSamplePointerCoords.size();
+ for (size_t i = 0; i < numSamples; i++) {
+ PointerCoords& c = mSamplePointerCoords.editItemAt(i);
+ float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ transformAngle(matrix, orientation, origin.x, origin.y));
+ vec2 xy = transformPoint(matrix, c.getX(), c.getY());
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+ }
+}
+
#ifdef __linux__
static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
float dsdx, dtdx, tx, dtdy, dsdy, ty;
@@ -606,6 +645,8 @@
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
mRawYCursorPosition = parcel->readFloat();
+ mDisplayWidth = parcel->readInt32();
+ mDisplayHeight = parcel->readInt32();
mDownTime = parcel->readInt64();
mPointerProperties.clear();
@@ -665,6 +706,8 @@
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
parcel->writeFloat(mRawYCursorPosition);
+ parcel->writeInt32(mDisplayWidth);
+ parcel->writeInt32(mDisplayHeight);
parcel->writeInt64(mDownTime);
for (size_t i = 0; i < pointerCount; i++) {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 31027b6..61d72ad 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -87,8 +87,10 @@
// Search system repository.
std::string path;
- // Treblized input device config files will be located /odm/usr or /vendor/usr.
- const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+ // 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")};
for (size_t i = 0; i < size(rootsForPartition); i++) {
if (rootsForPartition[i] == nullptr) {
continue;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 56a064b..dd1c462 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -96,28 +96,42 @@
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
- if (size() == actualSize) {
- switch (header.type) {
- case Type::KEY:
- return true;
- case Type::MOTION:
- return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
- case Type::FINISHED:
- return true;
- case Type::FOCUS:
- return true;
- case Type::CAPTURE:
- return true;
- case Type::DRAG:
- return true;
- case Type::TIMELINE:
- const nsecs_t gpuCompletedTime =
- body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
- const nsecs_t presentTime =
- body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
- return presentTime > gpuCompletedTime;
+ if (size() != actualSize) {
+ ALOGE("Received message of incorrect size %zu (expected %zu)", actualSize, size());
+ return false;
+ }
+
+ switch (header.type) {
+ case Type::KEY:
+ return true;
+ case Type::MOTION: {
+ const bool valid =
+ body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
+ if (!valid) {
+ ALOGE("Received invalid MOTION: pointerCount = %" PRIu32, body.motion.pointerCount);
+ }
+ return valid;
+ }
+ case Type::FINISHED:
+ case Type::FOCUS:
+ case Type::CAPTURE:
+ case Type::DRAG:
+ return true;
+ case Type::TIMELINE: {
+ const nsecs_t gpuCompletedTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ const bool valid = presentTime > gpuCompletedTime;
+ if (!valid) {
+ ALOGE("Received invalid TIMELINE: gpuCompletedTime = %" PRId64
+ " presentTime = %" PRId64,
+ gpuCompletedTime, presentTime);
+ }
+ return valid;
}
}
+ ALOGE("Invalid message type: %" PRIu32, header.type);
return false;
}
@@ -228,6 +242,10 @@
msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
// float yCursorPosition
msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
+ // int32_t displayW
+ msg->body.motion.displayWidth = body.motion.displayWidth;
+ // int32_t displayH
+ msg->body.motion.displayHeight = body.motion.displayHeight;
// uint32_t pointerCount
msg->body.motion.pointerCount = body.motion.pointerCount;
//struct Pointer pointers[MAX_POINTERS]
@@ -400,9 +418,7 @@
}
if (!msg->isValid(nRead)) {
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ received invalid message", mName.c_str());
-#endif
+ ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead);
return BAD_VALUE;
}
@@ -517,9 +533,9 @@
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords) {
+ float yPrecision, float xCursorPosition, float yCursorPosition, int32_t displayWidth,
+ int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+ const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
"publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -577,6 +593,8 @@
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
msg.body.motion.yCursorPosition = yCursorPosition;
+ msg.body.motion.displayWidth = displayWidth;
+ msg.body.motion.displayHeight = displayHeight;
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
@@ -1343,6 +1361,7 @@
msg->body.motion.buttonState, msg->body.motion.classification, transform,
msg->body.motion.xPrecision, msg->body.motion.yPrecision,
msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
+ msg->body.motion.displayWidth, msg->body.motion.displayHeight,
msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
pointerProperties, pointerCoords);
}
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 8546bbb..9947720 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -57,11 +57,12 @@
info.frameLeft == frameLeft && info.frameTop == frameTop &&
info.frameRight == frameRight && info.frameBottom == frameBottom &&
info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
- info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
- info.visible == visible && info.trustedOverlay == trustedOverlay &&
- info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
- info.hasWallpaper == hasWallpaper && info.paused == paused &&
- info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.transform == transform && info.displayWidth == displayWidth &&
+ info.displayHeight == displayHeight &&
+ info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
+ info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
+ info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
+ info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
info.packageName == packageName && info.inputFeatures == inputFeatures &&
info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
@@ -99,6 +100,8 @@
parcel->writeFloat(transform.dtdy()) ?:
parcel->writeFloat(transform.dsdy()) ?:
parcel->writeFloat(transform.ty()) ?:
+ parcel->writeInt32(displayWidth) ?:
+ parcel->writeInt32(displayHeight) ?:
parcel->writeBool(visible) ?:
parcel->writeBool(focusable) ?:
parcel->writeBool(hasWallpaper) ?:
@@ -153,6 +156,8 @@
parcel->readFloat(&dtdy) ?:
parcel->readFloat(&dsdy) ?:
parcel->readFloat(&ty) ?:
+ parcel->readInt32(&displayWidth) ?:
+ parcel->readInt32(&displayHeight) ?:
parcel->readBool(&visible) ?:
parcel->readBool(&focusable) ?:
parcel->readBool(&hasWallpaper) ?:
diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp
deleted file mode 100644
index 394da22..0000000
--- a/libs/input/LatencyStatistics.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include <input/LatencyStatistics.h>
-
-#include <android-base/chrono_utils.h>
-
-#include <cmath>
-#include <limits>
-
-namespace android {
-
-LatencyStatistics::LatencyStatistics(std::chrono::seconds period) : mReportPeriod(period) {
- reset();
-}
-
-/**
- * Add a raw value to the statistics
- */
-void LatencyStatistics::addValue(float value) {
- if (value < mMin) {
- mMin = value;
- }
- if (value > mMax) {
- mMax = value;
- }
- mSum += value;
- mSum2 += value * value;
- mCount++;
-}
-
-/**
- * Get the mean. Should not be called if no samples have been added.
- */
-float LatencyStatistics::getMean() {
- return mSum / mCount;
-}
-
-/**
- * Get the standard deviation. Should not be called if no samples have been added.
- */
-float LatencyStatistics::getStDev() {
- float mean = getMean();
- return sqrt(mSum2 / mCount - mean * mean);
-}
-
-float LatencyStatistics::getMin() {
- return mMin;
-}
-
-float LatencyStatistics::getMax() {
- return mMax;
-}
-
-size_t LatencyStatistics::getCount() {
- return mCount;
-}
-
-/**
- * Reset internal state. The variable 'when' is the time when the data collection started.
- * Call this to start a new data collection window.
- */
-void LatencyStatistics::reset() {
- mMax = std::numeric_limits<float>::lowest();
- mMin = std::numeric_limits<float>::max();
- mSum = 0;
- mSum2 = 0;
- mCount = 0;
- mLastReportTime = std::chrono::steady_clock::now();
-}
-
-bool LatencyStatistics::shouldReport() {
- std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime;
- return mCount != 0 && timeSinceReport >= mReportPeriod;
-}
-
-} // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index fe8a567..6ffc6a8 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -19,7 +19,6 @@
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
"InputWindow_test.cpp",
- "LatencyStatistics_test.cpp",
"TouchVideoFrame_test.cpp",
"VelocityTracker_test.cpp",
"VerifiedInputEvent_test.cpp",
@@ -49,8 +48,8 @@
cc_library_static {
name: "StructLayout_test",
srcs: ["StructLayout_test.cpp"],
+ compile_multilib: "both",
cflags: [
- "-O0",
"-Wall",
"-Werror",
"-Wextra",
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 601d8da..32b72ba 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -271,6 +271,7 @@
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
pointerCoords);
@@ -592,6 +593,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
pointerCoords);
float originalRawX = 0 + 3;
@@ -634,6 +636,97 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
+MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) {
+ std::vector<PointerProperties> pointerProperties;
+ pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
+ std::vector<PointerCoords> pointerCoords;
+ pointerCoords.emplace_back().clear();
+ pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ 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,
+ /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ /* buttonState */ 0, MotionClassification::NONE, transform,
+ /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
+ /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
+ pointerProperties.data(), pointerCoords.data());
+ return event;
+}
+
+TEST_F(MotionEventTest, ApplyTransform) {
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform identity;
+ ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(700, event.getRawX(0));
+ ASSERT_EQ(60, event.getRawY(0));
+ ASSERT_NE(event.getRawX(0), event.getX(0));
+ ASSERT_NE(event.getRawY(0), event.getY(0));
+
+ MotionEvent changedEvent = createTouchDownEvent(60, 100, identity);
+ const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
+ xform[0][1], xform[1][1], xform[2][1],
+ xform[0][2], xform[1][2], xform[2][2]};
+ changedEvent.applyTransform(rowMajor);
+
+ // transformContent effectively rotates the raw coordinates, so those should now include
+ // both rotation AND offset
+ ASSERT_EQ(720, changedEvent.getRawX(0));
+ ASSERT_EQ(100, changedEvent.getRawY(0));
+
+ // The transformed output should be the same then
+ ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001);
+ ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001);
+}
+
+TEST_F(MotionEventTest, RawCompatTransform) {
+ {
+ // Make sure raw is raw regardless of transform translation.
+ ui::Transform xform;
+ xform.set(20, 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(60, event.getRawX(0));
+ ASSERT_EQ(100, event.getRawY(0));
+ ASSERT_NE(event.getRawX(0), event.getX(0));
+ ASSERT_NE(event.getRawY(0), event.getY(0));
+ }
+
+ // Next check that getRaw contains rotation (for compatibility) but otherwise is still
+ // "Screen-space". The following tests check all 3 rotations.
+ {
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(700, event.getRawX(0));
+ ASSERT_EQ(60, event.getRawY(0));
+ ASSERT_NE(event.getRawX(0), event.getX(0));
+ ASSERT_NE(event.getRawY(0), event.getY(0));
+ }
+
+ {
+ // Same as above, but check rotate-180.
+ ui::Transform xform(ui::Transform::ROT_180, 400, 800);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(340, event.getRawX(0));
+ ASSERT_EQ(700, event.getRawY(0));
+ }
+
+ {
+ // Same as above, but check rotate-270.
+ ui::Transform xform(ui::Transform::ROT_270, 800, 400);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(100, event.getRawX(0));
+ ASSERT_EQ(340, event.getRawY(0));
+ }
+}
+
TEST_F(MotionEventTest, Initialize_SetsClassification) {
std::array<MotionClassification, 3> classifications = {
MotionClassification::NONE,
@@ -657,7 +750,8 @@
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
}
@@ -678,8 +772,10 @@
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
- 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 088e00b..a2cfaa1 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -162,6 +162,8 @@
constexpr float yPrecision = 0.5;
constexpr float xCursorPosition = 1.3;
constexpr float yCursorPosition = 50.6;
+ constexpr int32_t displayWidth = 1000;
+ constexpr int32_t displayHeight = 2000;
constexpr nsecs_t downTime = 3;
constexpr size_t pointerCount = 3;
constexpr nsecs_t eventTime = 4;
@@ -190,8 +192,9 @@
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
classification, transform, xPrecision, yPrecision,
- xCursorPosition, yCursorPosition, downTime, eventTime,
- pointerCount, pointerProperties, pointerCoords);
+ xCursorPosition, yCursorPosition, displayWidth,
+ displayHeight, downTime, eventTime, pointerCount,
+ pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -228,6 +231,8 @@
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
+ EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -455,7 +460,7 @@
status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
0, 0, 0, MotionClassification::NONE, identityTransform,
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
@@ -471,7 +476,7 @@
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
0, 0, 0, MotionClassification::NONE, identityTransform,
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
@@ -492,7 +497,7 @@
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
0, 0, 0, MotionClassification::NONE, identityTransform,
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index c18a17f..493f2f4 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -55,6 +55,8 @@
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+ i.displayWidth = 1000;
+ i.displayHeight = 2000;
i.visible = false;
i.focusable = false;
i.hasWallpaper = false;
@@ -91,6 +93,8 @@
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
ASSERT_EQ(i.transform, i2.transform);
+ ASSERT_EQ(i.displayWidth, i2.displayWidth);
+ ASSERT_EQ(i.displayHeight, i2.displayHeight);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
diff --git a/libs/input/tests/LatencyStatistics_test.cpp b/libs/input/tests/LatencyStatistics_test.cpp
deleted file mode 100644
index eb12d4e..0000000
--- a/libs/input/tests/LatencyStatistics_test.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include <gtest/gtest.h>
-#include <input/LatencyStatistics.h>
-#include <cmath>
-#include <limits>
-#include <thread>
-
-namespace android {
-namespace test {
-
-TEST(LatencyStatisticsTest, ResetStats) {
- LatencyStatistics stats{5min};
- stats.addValue(5.0);
- stats.addValue(19.3);
- stats.addValue(20);
- stats.reset();
-
- ASSERT_EQ(stats.getCount(), 0u);
- ASSERT_EQ(std::isnan(stats.getStDev()), true);
- ASSERT_EQ(std::isnan(stats.getMean()), true);
-}
-
-TEST(LatencyStatisticsTest, AddStatsValue) {
- LatencyStatistics stats{5min};
- stats.addValue(5.0);
-
- ASSERT_EQ(stats.getMin(), 5.0);
- ASSERT_EQ(stats.getMax(), 5.0);
- ASSERT_EQ(stats.getCount(), 1u);
- ASSERT_EQ(stats.getMean(), 5.0);
- ASSERT_EQ(stats.getStDev(), 0.0);
-}
-
-TEST(LatencyStatisticsTest, AddMultipleStatsValue) {
- LatencyStatistics stats{5min};
- stats.addValue(4.0);
- stats.addValue(6.0);
- stats.addValue(8.0);
- stats.addValue(10.0);
-
- float stdev = stats.getStDev();
-
- ASSERT_EQ(stats.getMin(), 4.0);
- ASSERT_EQ(stats.getMax(), 10.0);
- ASSERT_EQ(stats.getCount(), 4u);
- ASSERT_EQ(stats.getMean(), 7.0);
- ASSERT_EQ(stdev * stdev, 5.0);
-}
-
-TEST(LatencyStatisticsTest, ShouldReportStats) {
- LatencyStatistics stats{0min};
- stats.addValue(5.0);
-
- std::this_thread::sleep_for(1us);
-
- ASSERT_EQ(stats.shouldReport(), true);
-}
-
-} // namespace test
-} // namespace android
\ No newline at end of file
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 585779e..5861d55 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -74,9 +74,11 @@
CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
- CHECK_OFFSET(InputMessage::Body::Motion, empty3, 140);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
+ CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 140);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 144);
+ CHECK_OFFSET(InputMessage::Body::Motion, empty3, 148);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index d049d05..aefc2ec 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -183,7 +183,8 @@
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
events.emplace_back(event);
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 36f87b8..f79098c 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -46,8 +46,10 @@
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/,
- 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ 100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
return event;
}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 8675439..9286009 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -60,7 +60,13 @@
cc_library {
name: "libnativewindow",
- llndk_stubs: "libnativewindow.llndk",
+ llndk: {
+ symbol_file: "libnativewindow.map.txt",
+ unversioned: true,
+ override_export_include_dirs: [
+ "include"
+ ],
+ },
export_include_dirs: [
"include",
"include-private",
@@ -115,11 +121,4 @@
},
}
-llndk_library {
- name: "libnativewindow.llndk",
- symbol_file: "libnativewindow.map.txt",
- unversioned: true,
- export_include_dirs: ["include"],
-}
-
subdirs = ["tests"]
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 61b3f94..f0e1c4d 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -300,8 +300,13 @@
* this ANativeWindow is consumed by something other than the system compositor,
* e.g. a media codec, this call has no effect.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* Available since API level 31.
*
+ * \param window pointer to an ANativeWindow object.
+ *
* \param frameRate The intended frame rate of this window, in frames per
* second. 0 is a special value that indicates the app will accept the system's
* choice for the display frame rate, which is the default behavior if this
@@ -309,15 +314,16 @@
* valid refresh rate for this device's display - e.g., it's fine to pass 30fps
* to a device that can only run the display at 60fps.
*
- * \param window pointer to an ANativeWindow object.
- *
* \param compatibility The frame rate compatibility of this window. The
* compatibility value may influence the system's choice of display refresh
* rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
+ * This parameter is ignored when frameRate is 0.
*
- * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * window should be seamless.
* A seamless transition is one that doesn't have any visual interruptions, such as a black
* screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
+ * This parameter is ignored when frameRate is 0.
*
* \return 0 for success, -EINVAL if the window, frame rate, or compatibility
* value are invalid.
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index cc82bb4..935eded 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -257,6 +257,7 @@
NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */
NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO = 48, /* private */
NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT = 49, /* private */
+ NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2 = 50, /* private */
// clang-format on
};
@@ -1067,6 +1068,31 @@
}
/**
+ * Retrieves the last queued buffer for this window, along with the fence that
+ * fires when the buffer is ready to be read. The cropRect & transform should be applied to the
+ * buffer's content.
+ *
+ * If there was no buffer previously queued, then outBuffer will be NULL and
+ * the value of outFence will be -1.
+ *
+ * Note that if outBuffer is not NULL, then the caller will hold a reference
+ * onto the buffer. Accordingly, the caller must call AHardwareBuffer_release
+ * when the buffer is no longer needed so that the system may reclaim the
+ * buffer.
+ *
+ * \return NO_ERROR on success.
+ * \return NO_MEMORY if there was insufficient memory.
+ * \return STATUS_UNKNOWN_TRANSACTION if this ANativeWindow doesn't support this method, callers
+ * should fall back to ANativeWindow_getLastQueuedBuffer instead.
+ */
+static inline int ANativeWindow_getLastQueuedBuffer2(ANativeWindow* window,
+ AHardwareBuffer** outBuffer, int* outFence,
+ ARect* outCropRect, uint32_t* outTransform) {
+ return window->perform(window, NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2, outBuffer, outFence,
+ outCropRect, outTransform);
+}
+
+/**
* Retrieves an identifier for the next frame to be queued by this window.
*
* \return the next frame id.
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
index dd38224..3243a6b 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -1,14 +1,49 @@
+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"],
+}
+
+aidl_interface {
+ name: "framework-permission-aidl",
+ unstable: true,
+ local_include_dir: "aidl",
+ backend: {
+ ndk: {
+ enabled: false
+ }
+ },
+ srcs: [
+ "aidl/android/content/AttributionSourceState.aidl",
+ "aidl/android/permission/IPermissionChecker.aidl",
+ ],
+}
+
cc_library_shared {
name: "libpermission",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
srcs: [
"AppOpsManager.cpp",
"IAppOpsCallback.cpp",
"IAppOpsService.cpp",
+ "android/permission/PermissionChecker.cpp",
],
export_include_dirs: ["include"],
shared_libs: [
- "libbinder",
- "liblog",
"libutils",
+ "libbinder",
+ "libcutils",
+ "liblog",
],
+ static_libs: [
+ "framework-permission-aidl-cpp",
+ ],
+ export_static_lib_headers: ["framework-permission-aidl-cpp"],
}
diff --git a/libs/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl
new file mode 100644
index 0000000..b6e54bf
--- /dev/null
+++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.content;
+
+/**
+ * Payload for the {@link AttributionSource} class needed to interoperate
+ * with different languages.
+ *
+ * {@hide}
+ */
+parcelable AttributionSourceState {
+ /** The UID that is accessing the permission protected data. */
+ int uid;
+ /** The package that is accessing the permission protected data. */
+ @nullable @utf8InCpp String packageName;
+ /** The attribution tag of the app accessing the permission protected data. */
+ @nullable @utf8InCpp String attributionTag;
+ /** Unique token for that source. */
+ @nullable IBinder token;
+ /** Permissions that should be considered revoked regardless if granted. */
+ @nullable @utf8InCpp String[] renouncedPermissions;
+ /** The next app to receive the permission protected data. */
+ // TODO: We use an array as a workaround - the C++ backend doesn't
+ // support referring to the parcelable as it expects ctor/dtor
+ @nullable AttributionSourceState[] next;
+}
diff --git a/libs/permission/aidl/android/permission/IPermissionChecker.aidl b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
new file mode 100644
index 0000000..1f0e32d
--- /dev/null
+++ b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.permission;
+
+import android.content.AttributionSourceState;
+
+/**
+ * Interface to communicate directly with the permission checker service.
+ */
+interface IPermissionChecker {
+ const int PERMISSION_GRANTED = 0;
+ const int PERMISSION_SOFT_DENIED = 1;
+ const int PERMISSION_HARD_DENIED = 2;
+
+ int checkPermission(String permission, in AttributionSourceState attributionSource,
+ @nullable String message, boolean forDataDelivery, boolean startDataDelivery,
+ boolean fromDatasource);
+
+ void finishDataDelivery(String op, in AttributionSourceState attributionSource);
+
+ int checkOp(int op, in AttributionSourceState attributionSource,
+ String message, boolean forDataDelivery, boolean startDataDelivery);
+}
diff --git a/libs/permission/android/permission/PermissionChecker.cpp b/libs/permission/android/permission/PermissionChecker.cpp
new file mode 100644
index 0000000..a8083ee
--- /dev/null
+++ b/libs/permission/android/permission/PermissionChecker.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 <mutex>
+#include <include/android/permission/PermissionChecker.h>
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/SystemClock.h>
+
+#include <sys/types.h>
+#include <private/android_filesystem_config.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "PermissionChecker"
+
+namespace android {
+
+using android::content::AttributionSourceState;
+
+PermissionChecker::PermissionChecker()
+{
+}
+
+sp<IPermissionChecker> PermissionChecker::getService()
+{
+ static String16 permission_checker("permission_checker");
+
+ std::lock_guard<Mutex> scoped_lock(mLock);
+ int64_t startTime = 0;
+ sp<IPermissionChecker> service = mService;
+ while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(permission_checker);
+ if (binder == nullptr) {
+ // Wait for the permission checker service to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ ALOGW("Waiting for permission checker service");
+ } else if ((uptimeMillis() - startTime) > 10000) {
+ ALOGE("Waiting too long for permission checker service, giving up");
+ service = nullptr;
+ break;
+ }
+ sleep(1);
+ } else {
+ mService = interface_cast<IPermissionChecker>(binder);
+ }
+ }
+ return mService;
+}
+
+PermissionChecker::PermissionResult
+ PermissionChecker::checkPermissionForDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message)
+{
+ return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
+ /*forDataDelivery*/ true, /*startDataDelivery*/ false,/*fromDatasource*/ true));
+}
+
+PermissionChecker::PermissionResult
+ PermissionChecker::checkPermissionForStartDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message)
+{
+ return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
+ /*forDataDelivery*/ true, /*startDataDelivery*/ true, /*fromDatasource*/ true));
+}
+
+void PermissionChecker::finishDataDelivery(const String16& op,
+ AttributionSourceState& attributionSource)
+{
+ sp<IPermissionChecker> service = getService();
+ if (service != nullptr) {
+ binder::Status status = service->finishDataDelivery(op, attributionSource);
+ if (!status.isOk()) {
+ ALOGE("finishDataDelivery failed: %s", status.exceptionMessage().c_str());
+ }
+ }
+}
+
+int32_t PermissionChecker::checkPermission(const String16& permission,
+ AttributionSourceState& attributionSource, const String16& message,
+ bool forDataDelivery, bool startDataDelivery, bool fromDatasource)
+{
+ sp<IPermissionChecker> service = getService();
+ if (service != nullptr) {
+ int32_t result;
+ binder::Status status = service->checkPermission(permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource, &result);
+ if (status.isOk()) {
+ return result;
+ }
+ ALOGE("checkPermission failed: %s", status.exceptionMessage().c_str());
+ }
+ return PERMISSION_DENIED;
+}
+
+} // namespace android
diff --git a/libs/permission/include/android/permission/PermissionChecker.h b/libs/permission/include/android/permission/PermissionChecker.h
new file mode 100644
index 0000000..20ab51f
--- /dev/null
+++ b/libs/permission/include/android/permission/PermissionChecker.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/content/AttributionSourceState.h>
+#include <android/permission/IPermissionChecker.h>
+
+#include <utils/threads.h>
+
+#include <optional>
+
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+using android::content::AttributionSourceState;
+using android::permission::IPermissionChecker;
+
+class PermissionChecker
+{
+public:
+
+ enum PermissionResult {
+
+ /**
+ * The permission is granted.
+ */
+ PERMISSION_GRANTED = IPermissionChecker::PERMISSION_GRANTED,
+
+ /**
+ * The permission is denied. Applicable only to runtime and app op permissions.
+ *
+ * Returned when:
+ * - the runtime permission is granted, but the corresponding app op is denied
+ * for runtime permissions.
+ * - the app ops is ignored for app op permissions.
+ *
+ */
+ PERMISSION_SOFT_DENIED = IPermissionChecker::PERMISSION_SOFT_DENIED,
+
+ /**
+ * The permission is denied.
+ *
+ * Returned when:
+ * - the permission is denied for non app op permissions.
+ * - the app op is denied or app op is AppOpsManager#MODE_DEFAULT and permission is denied.
+ */
+ PERMISSION_HARD_DENIED = IPermissionChecker::PERMISSION_HARD_DENIED
+ };
+
+ PermissionChecker();
+
+ /**
+ * Checks whether a given data access chain described by the given attribution source
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. Call this method if you are the datasource which would not blame you for
+ * access to the data since you are the data. Note that the attribution source chain
+ *
+ * NOTE: The attribution source should be for yourself with its next attribution
+ * source being the app that would receive the data from you.
+ *
+ * NOTE: Use this method only for permission checks at the point where you will deliver
+ * the permission protected data to clients.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource The attribution chain to check.
+ * @param message A message describing the reason the permission was checked.
+ * @return The permission check result which is either PERMISSION_GRANTED,
+ * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+ */
+ PermissionChecker::PermissionResult checkPermissionForDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message);
+
+ /**
+ * Checks whether a given data access chain described by the given attribution source
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. The app ops are also marked as started. This is useful for long running
+ * permissions like camera and microphone.
+ *
+ * NOTE: The attribution source should be for yourself with its next attribution
+ * source being the app that would receive the data from you.
+ *
+ * NOTE: Use this method only for permission checks at the point where you will deliver
+ * the permission protected data to clients.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource The attribution chain to check.
+ * @param message A message describing the reason the permission was checked.
+ * @return The permission check result which is either PERMISSION_GRANTED,
+ * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+ */
+ PermissionResult checkPermissionForStartDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message);
+
+ /**
+ * Finishes an ongoing op for data access chain described by the given
+ * attribution source.
+ *
+ * @param op The op to finish.
+ * @param attributionSource The attribution chain for which to finish data delivery.
+ */
+ void finishDataDelivery(const String16& op, AttributionSourceState& attributionSource);
+
+private:
+ Mutex mLock;
+ sp<IPermissionChecker> mService;
+ sp<IPermissionChecker> getService();
+
+ int32_t checkPermission(const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message, bool forDataDelivery, bool startDataDelivery,
+ bool fromDatasource);
+};
+
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index c048cbe..e3d705f 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -146,7 +146,8 @@
OP_UWB_RANGING = 112,
OP_ACTIVITY_RECOGNITION_SOURCE = 113,
OP_BLUETOOTH_ADVERTISE = 114,
- _NUM_OP = 115
+ OP_RECORD_INCOMING_PHONE_AUDIO = 115,
+ _NUM_OP = 116
};
AppOpsManager();
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index f395ab4..570c7bc 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -95,6 +95,7 @@
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/StretchShaderFactory.cpp"
],
}
@@ -117,7 +118,7 @@
include_dirs: [
"external/skia/src/gpu",
],
- whole_static_libs: ["libskia"],
+ whole_static_libs: ["libskia_renderengine"],
lto: {
thin: true,
},
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index d87315f..3c58238 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -249,7 +249,7 @@
// initialize EGL for the default display
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(display, nullptr, nullptr)) {
- LOG_ALWAYS_FATAL("failed to initialize EGL");
+ LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError());
}
const auto eglVersion = eglQueryString(display, EGL_VERSION);
@@ -1233,8 +1233,10 @@
}
}
- mState.maxMasteringLuminance = layer->source.buffer.maxMasteringLuminance;
- mState.maxContentLuminance = layer->source.buffer.maxContentLuminance;
+ // Ensure luminance is at least 100 nits to avoid div-by-zero
+ const float maxLuminance = std::max(100.f, layer->source.buffer.maxLuminanceNits);
+ mState.maxMasteringLuminance = maxLuminance;
+ mState.maxContentLuminance = maxLuminance;
mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
const FloatRect bounds = layer->geometry.boundaries;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index a637796..53fa622 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -60,6 +60,9 @@
// capture of a device in landscape while the buffer is in portrait
// orientation.
uint32_t orientation = ui::Transform::ROT_0;
+
+ // SDR white point, -1f if unknown
+ float sdrWhitePointNits = -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 c54c5ba..715f3f8 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -64,8 +64,8 @@
// HDR color-space setting for Y410.
bool isY410BT2020 = false;
- float maxMasteringLuminance = 0.0;
- float maxContentLuminance = 0.0;
+
+ float maxLuminanceNits = 0.0;
};
// Metadata describing the layer geometry.
@@ -107,6 +107,9 @@
* material design guidelines.
*/
struct ShadowSettings {
+ // Boundaries of the shadow.
+ FloatRect boundaries = FloatRect();
+
// Color to the ambient shadow. The alpha is premultiplied.
vec4 ambientColor = vec4();
@@ -150,12 +153,20 @@
// True if blending will be forced to be disabled.
bool disableBlending = false;
+ // If true, then this layer casts a shadow and/or blurs behind it, but it does
+ // not otherwise draw any of the layer's other contents.
+ bool skipContentDraw = false;
+
ShadowSettings shadow;
int backgroundBlurRadius = 0;
std::vector<BlurRegion> blurRegions;
+ // Transform matrix used to convert the blurRegions geometry into the same
+ // coordinate space as LayerSettings.geometry
+ mat4 blurRegionTransform = mat4();
+
StretchEffect stretchEffect;
// Name associated with the layer for debugging purposes.
@@ -171,8 +182,7 @@
lhs.textureTransform == rhs.textureTransform &&
lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
- lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
- lhs.maxContentLuminance == rhs.maxContentLuminance;
+ lhs.maxLuminanceNits == rhs.maxLuminanceNits;
}
static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
@@ -186,21 +196,10 @@
}
static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
- return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
- lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
- lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
-}
-
-static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
- return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
- lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
- lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
- lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
- lhs.bottom == rhs.bottom;
-}
-
-static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
- return !(lhs == rhs);
+ return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
+ lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
+ lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
+ lhs.casterIsTranslucent == rhs.casterIsTranslucent;
}
static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
@@ -217,8 +216,11 @@
return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
- lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
- lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+ lhs.disableBlending == rhs.disableBlending &&
+ lhs.skipContentDraw == rhs.skipContentDraw && lhs.shadow == rhs.shadow &&
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+ lhs.blurRegionTransform == rhs.blurRegionTransform &&
+ lhs.stretchEffect == rhs.stretchEffect;
}
// Defining PrintTo helps with Google Tests.
@@ -233,8 +235,7 @@
*os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
*os << "\n .isOpaque = " << settings.isOpaque;
*os << "\n .isY410BT2020 = " << settings.isY410BT2020;
- *os << "\n .maxMasteringLuminance = " << settings.maxMasteringLuminance;
- *os << "\n .maxContentLuminance = " << settings.maxContentLuminance;
+ *os << "\n .maxLuminanceNits = " << settings.maxLuminanceNits;
*os << "\n}";
}
@@ -259,6 +260,8 @@
static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
*os << "ShadowSettings {";
+ *os << "\n .boundaries = ";
+ PrintTo(settings.boundaries, os);
*os << "\n .ambientColor = " << settings.ambientColor;
*os << "\n .spotColor = " << settings.spotColor;
*os << "\n .lightPos = " << settings.lightPos;
@@ -268,8 +271,23 @@
*os << "\n}";
}
+static inline void PrintTo(const StretchEffect& effect, ::std::ostream* os) {
+ *os << "StretchEffect {";
+ *os << "\n .width = " << effect.width;
+ *os << "\n .height = " << effect.height;
+ *os << "\n .vectorX = " << effect.vectorX;
+ *os << "\n .vectorY = " << effect.vectorY;
+ *os << "\n .maxAmountX = " << effect.maxAmountX;
+ *os << "\n .maxAmountY = " << effect.maxAmountY;
+ *os << "\n .mappedLeft = " << effect.mappedChildBounds.left;
+ *os << "\n .mappedTop = " << effect.mappedChildBounds.top;
+ *os << "\n .mappedRight = " << effect.mappedChildBounds.right;
+ *os << "\n .mappedBottom = " << effect.mappedChildBounds.bottom;
+ *os << "\n}";
+}
+
static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
- *os << "LayerSettings {";
+ *os << "LayerSettings for '" << settings.name.c_str() << "' {";
*os << "\n .geometry = ";
PrintTo(settings.geometry, os);
*os << "\n .source = ";
@@ -279,6 +297,7 @@
PrintTo(settings.sourceDataspace, os);
*os << "\n .colorTransform = " << settings.colorTransform;
*os << "\n .disableBlending = " << settings.disableBlending;
+ *os << "\n .skipContentDraw = " << settings.skipContentDraw;
*os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius;
for (auto blurRegion : settings.blurRegions) {
*os << "\n";
@@ -286,6 +305,8 @@
}
*os << "\n .shadow = ";
PrintTo(settings.shadow, os);
+ *os << "\n .stretchEffect = ";
+ PrintTo(settings.stretchEffect, os);
*os << "\n}";
}
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index c8a0f0a..ddaa7c7 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -308,7 +308,8 @@
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
- RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
+ RenderEngine::RenderEngineType renderEngineType =
+ RenderEngine::RenderEngineType::SKIA_GL_THREADED;
};
} // namespace renderengine
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index c535597..8ae69de 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -29,7 +29,8 @@
namespace skia {
AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
- bool isRender) {
+ bool isOutputBuffer)
+ : mIsOutputBuffer(isOutputBuffer) {
ATRACE_CALL();
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
@@ -40,8 +41,12 @@
GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height,
&mDeleteProc, &mUpdateProc, &mImageCtx,
createProtectedImage, backendFormat,
- isRender);
+ isOutputBuffer);
mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
+ ALOGE_IF(!mBackendTexture.isValid(),
+ "Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d isWriteable:%d "
+ "format:%d",
+ this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
}
void AutoBackendTexture::unref(bool releaseLocalResources) {
@@ -92,13 +97,16 @@
mImage = image;
mDataspace = dataspace;
- LOG_ALWAYS_FATAL_IF(mImage == nullptr, "Unable to generate SkImage from buffer");
+ LOG_ALWAYS_FATAL_IF(mImage == nullptr,
+ "Unable to generate SkImage. isTextureValid:%d dataspace:%d",
+ mBackendTexture.isValid(), dataspace);
return mImage;
}
sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace,
GrDirectContext* context) {
ATRACE_CALL();
+ LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
if (!mSurface.get() || mDataspace != dataspace) {
sk_sp<SkSurface> surface =
SkSurface::MakeFromBackendTexture(context, mBackendTexture,
@@ -113,10 +121,12 @@
}
mDataspace = dataspace;
- LOG_ALWAYS_FATAL_IF(mSurface == nullptr, "Unable to generate SkSurface");
+ LOG_ALWAYS_FATAL_IF(mSurface == nullptr,
+ "Unable to generate SkSurface. isTextureValid:%d dataspace:%d",
+ mBackendTexture.isValid(), dataspace);
return mSurface;
}
} // namespace skia
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 2d61cf8..3133de6 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -41,34 +41,42 @@
// of shared ownership with Skia objects, so we wrap it here instead.
class LocalRef {
public:
- LocalRef(AutoBackendTexture* texture) { setTexture(texture); }
-
- ~LocalRef() {
- // Destroying the texture is the same as setting it to null
- setTexture(nullptr);
+ LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer) {
+ mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer);
+ mTexture->ref();
}
- AutoBackendTexture* getTexture() const { return mTexture; }
+ ~LocalRef() {
+ if (mTexture != nullptr) {
+ mTexture->unref(true);
+ }
+ }
+
+ // Makes a new SkImage from the texture content.
+ // As SkImages are immutable but buffer content is not, we create
+ // a new SkImage every time.
+ sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
+ GrDirectContext* context) {
+ return mTexture->makeImage(dataspace, alphaType, context);
+ }
+
+ // Makes a new SkSurface from the texture content, if needed.
+ sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context) {
+ return mTexture->getOrCreateSurface(dataspace, context);
+ }
DISALLOW_COPY_AND_ASSIGN(LocalRef);
private:
- // Sets the texture to locally ref-track.
- void setTexture(AutoBackendTexture* texture) {
- if (mTexture != nullptr) {
- mTexture->unref(true);
- }
-
- mTexture = texture;
- if (mTexture != nullptr) {
- mTexture->ref();
- }
- }
AutoBackendTexture* mTexture = nullptr;
};
+private:
// Creates a GrBackendTexture whose contents come from the provided buffer.
- AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isRender);
+ AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer);
+
+ // The only way to invoke dtor is with unref, when mUsageCount is 0.
+ ~AutoBackendTexture() {}
void ref() { mUsageCount++; }
@@ -85,10 +93,6 @@
// Makes a new SkSurface from the texture content, if needed.
sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context);
-private:
- // The only way to invoke dtor is with unref, when mUsageCount is 0.
- ~AutoBackendTexture() {}
-
GrBackendTexture mBackendTexture;
GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
@@ -99,6 +103,7 @@
int mUsageCount = 0;
+ const bool mIsOutputBuffer;
sk_sp<SkImage> mImage = nullptr;
sk_sp<SkSurface> mSurface = nullptr;
ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN;
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 1c2b2fc..77e01f4 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -38,11 +38,10 @@
0.f, 0.f, 1.f, 0.f,
67.3f, 52.2f, 0.f, 1.f);
// clang-format on
-// When choosing dataspaces below, whether the match the destination or not determined whether
-// a color correction effect is added to the shader. There may be other additional shader details
-// for particular color spaces.
-// TODO(b/184842383) figure out which color related shaders are necessary
+// When setting layer.sourceDataspace, whether it matches the destination or not determines whether
+// a color correction effect is added to the shader.
constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
+constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3;
} // namespace
static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -105,8 +104,7 @@
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxMasteringLuminance = 1000.f,
- .maxContentLuminance = 1000.f,
+ .maxLuminanceNits = 1000.f,
}},
};
@@ -115,20 +113,24 @@
// Test both drawRect and drawRRect
auto layers = std::vector<const LayerSettings*>{&layer};
- for (bool identity : {true, false}) {
- layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
- // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
- // due to animating corner radius.
- // For the non-idenity matrix, only the large corner radius will create a new shader.
- for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
- // roundedCornersCrop is always set, but it is this radius that triggers the behavior
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
- for (bool isOpaque : {true, false}) {
- layer.source.buffer.isOpaque = isOpaque;
- for (auto alpha : {half(.23999f), half(1.0f)}) {
- layer.alpha = alpha;
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
+ layer.sourceDataspace = dataspace;
+ for (bool identity : {true, false}) {
+ layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
+ // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
+ // due to animating corner radius.
+ // For the non-idenity matrix, only the large corner radius will create a new shader.
+ for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
+ // roundedCornersCrop is always set, but it is this radius that triggers the
+ // behavior
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ for (bool isOpaque : {true, false}) {
+ layer.source.buffer.isOpaque = isOpaque;
+ for (auto alpha : {half(.23999f), half(1.0f)}) {
+ layer.alpha = alpha;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
}
}
}
@@ -182,6 +184,54 @@
}
}
+// The unique feature of these layers is that the boundary is slightly smaller than the rounded
+// rect crop, so the rounded edges intersect that boundary and require a different clipping method.
+static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height() - 20); // boundary is smaller
+
+ // clang-format off
+ const auto symmetric = mat4(0.9f, 0.f, 0.f, 0.f,
+ 0.f, 0.9f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 8.8f, 8.1f, 0.f, 1.f);
+ const auto asymmetric = mat4(0.9f, 0.f, 0.f, 0.f,
+ 0.f, 0.7f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 8.8f, 8.1f, 0.f, 1.f);
+
+ // clang-format on
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersRadius = 27, // larger than the 20 above.
+ .roundedCornersCrop =
+ FloatRect(0, 0, displayRect.width(), displayRect.height()),
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .isOpaque = 0,
+ .maxLuminanceNits = 1000.f,
+ }},
+ .sourceDataspace = kOtherDataSpace,
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (auto transform : {symmetric, asymmetric}) {
+ layer.geometry.positionTransform = transform;
+ // In real use, I saw alpha of 1.0 and 0.999, probably a mistake, but cache both shaders.
+ for (float alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha,
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+ }
+}
+
//
// The collection of shaders cached here were found by using perfetto to record shader compiles
// during actions that involve RenderEngine, logging the layer settings, and the shader code
@@ -200,60 +250,70 @@
if (previousCount) {
ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
}
- const nsecs_t timeBefore = systemTime();
- // The dimensions should not matter, so long as we draw inside them.
- const Rect displayRect(0, 0, 1080, 2340);
- DisplaySettings display{
- .physicalDisplay = displayRect,
- .clip = displayRect,
- .maxLuminance = 500,
- .outputDataspace = kDestDataSpace,
- };
- const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ // The loop is beneficial for debugging and should otherwise be optimized out by the compiler.
+ // Adding additional bounds to the loop is useful for verifying that the size of the dst buffer
+ // does not impact the shader compilation counts by triggering different behaviors in RE/Skia.
+ for (SkSize bounds : {SkSize::Make(128, 128), /*SkSize::Make(1080, 2340)*/}) {
+ const nsecs_t timeBefore = systemTime();
+ // The dimensions should not matter, so long as we draw inside them.
+ const Rect displayRect(0, 0, bounds.fWidth, bounds.fHeight);
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = kDestDataSpace,
+ };
- sp<GraphicBuffer> dstBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usage, "primeShaderCache_dst");
+ const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- const auto dstTexture = std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
- ExternalTexture::Usage::WRITEABLE);
- // This buffer will be the source for the call to drawImageLayers. Draw
- // something to it as a placeholder for what an app draws. We should draw
- // something, but the details are not important. Make use of the shadow layer drawing step
- // to populate it.
- sp<GraphicBuffer> srcBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usage, "drawImageLayer_src");
+ sp<GraphicBuffer> dstBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usage, "primeShaderCache_dst");
- const auto srcTexture =
- std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
- ExternalTexture::Usage::READABLE |
- ExternalTexture::Usage::WRITEABLE);
+ const auto dstTexture =
+ std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
+ ExternalTexture::Usage::WRITEABLE);
+ // This buffer will be the source for the call to drawImageLayers. Draw
+ // something to it as a placeholder for what an app draws. We should draw
+ // something, but the details are not important. Make use of the shadow layer drawing step
+ // to populate it.
+ sp<GraphicBuffer> srcBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usage, "drawImageLayer_src");
- drawSolidLayers(renderengine, display, dstTexture);
- drawShadowLayers(renderengine, display, srcTexture);
- drawBlurLayers(renderengine, display, dstTexture);
- // The majority of shaders are related to sampling images.
- drawImageLayers(renderengine, display, dstTexture, srcTexture);
+ const auto srcTexture =
+ std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE |
+ ExternalTexture::Usage::WRITEABLE);
- // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
- const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
+ drawSolidLayers(renderengine, display, dstTexture);
+ drawShadowLayers(renderengine, display, srcTexture);
+ drawBlurLayers(renderengine, display, dstTexture);
+ // The majority of shaders are related to sampling images.
+ drawImageLayers(renderengine, display, dstTexture, srcTexture);
- sp<GraphicBuffer> externalBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usageExternal, "primeShaderCache_external");
- const auto externalTexture =
- std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
- ExternalTexture::Usage::READABLE);
- // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
- // between 6 and 8 will occur in real uses.
- drawImageLayers(renderengine, display, dstTexture, externalTexture);
+ // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
- const nsecs_t timeAfter = systemTime();
- const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
- const int shadersCompiled = renderengine->reportShadersCompiled();
- ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
+ sp<GraphicBuffer> externalBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usageExternal, "primeShaderCache_external");
+ const auto externalTexture =
+ std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE);
+ // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
+ // between 6 and 8 will occur in real uses.
+ drawImageLayers(renderengine, display, dstTexture, externalTexture);
+
+ // Draw layers for b/185569240.
+ drawClippedLayers(renderengine, display, dstTexture, externalTexture);
+
+ const nsecs_t timeAfter = systemTime();
+ const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ const int shadersCompiled = renderengine->reportShadersCompiled();
+ ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
+ }
}
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 37d98a3..9f6ecaa 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -56,6 +56,7 @@
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
#include "skia/debug/SkiaMemoryReporter.h"
+#include "skia/filters/StretchShaderFactory.h"
#include "system/graphics-base-v1.0.h"
namespace {
@@ -365,6 +366,10 @@
return mProtectedEGLContext != EGL_NO_CONTEXT;
}
+GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const {
+ return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
+}
+
bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
if (useProtectedContext == mInProtectedContext) {
return true;
@@ -372,6 +377,12 @@
if (useProtectedContext && !supportsProtectedContent()) {
return false;
}
+
+ // release any scratch resources before switching into a new mode
+ if (getActiveGrContext()) {
+ getActiveGrContext()->purgeUnlockedResources(true);
+ }
+
const EGLSurface surface =
useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
@@ -379,6 +390,11 @@
if (success) {
mInProtectedContext = useProtectedContext;
+ // given that we are sharing the same thread between two GrContexts we need to
+ // make sure that the thread state is reset when switching between the two.
+ if (getActiveGrContext()) {
+ getActiveGrContext()->resetContext();
+ }
}
return success;
}
@@ -489,32 +505,31 @@
if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
return;
}
- ATRACE_CALL();
-
- // We need to switch the currently bound context if the buffer is protected but the current
- // context is not. The current state must then be restored after the buffer is cached.
- const bool protectedContextState = mInProtectedContext;
- if (!useProtectedContext(protectedContextState ||
- (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) {
- ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
+ // We currently don't attempt to map a buffer if the buffer contains protected content
+ // or we are using a protected context because GPU resources for protected buffers is
+ // much more limited.
+ const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+ if (isProtectedBuffer || mInProtectedContext) {
return;
}
+ ATRACE_CALL();
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+ // If we were to support caching protected buffers then we will need to switch the currently
+ // bound context if we are not already using the protected context (and subsequently switch
+ // back after the buffer is cached).
+ auto grContext = getActiveGrContext();
+ auto& cache = mTextureCache;
std::lock_guard<std::mutex> lock(mRenderingMutex);
mGraphicBufferExternalRefs[buffer->getId()]++;
if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(
- new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(),
- isRenderable));
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
+ buffer->toAHardwareBuffer(),
+ isRenderable);
cache.insert({buffer->getId(), imageTextureRef});
}
- // restore the original state of the protected context if necessary
- useProtectedContext(protectedContextState);
}
void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
@@ -541,14 +556,23 @@
}
}
-sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
- const LayerSettings* layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect) {
- if (layer->stretchEffect.hasEffect()) {
- // TODO: Implement
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
+ sk_sp<SkShader> shader,
+ const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
+ bool requiresLinearEffect) {
+ const auto stretchEffect = layer->stretchEffect;
+ // 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
+ if (stretchEffect.hasEffect()) {
+ const auto targetBuffer = layer->source.buffer.buffer;
+ const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+ if (graphicBuffer && shader) {
+ shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
+ }
}
+
if (requiresLinearEffect) {
const ui::Dataspace inputDataspace =
mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
@@ -567,10 +591,14 @@
} 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,
- layer->source.buffer.maxMasteringLuminance,
- layer->source.buffer.maxContentLuminance);
+ display.maxLuminance, maxLuminance);
}
return shader;
}
@@ -634,6 +662,17 @@
int mSaveCount;
};
+static SkRRect getBlurRRect(const BlurRegion& region) {
+ const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
+ const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
+ SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
+ SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
+ SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
+ SkRRect roundedRect;
+ roundedRect.setRectRadii(rect, radii);
+ return roundedRect;
+}
+
status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
@@ -662,22 +701,23 @@
validateOutputBufferUsage(buffer->getBuffer());
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
surfaceTextureRef = it->second;
} else {
- surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(
- new AutoBackendTexture(grContext.get(), buffer->getBuffer()->toAHardwareBuffer(),
- true));
+ surfaceTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
+ buffer->getBuffer()
+ ->toAHardwareBuffer(),
+ true);
}
const ui::Dataspace dstDataspace =
mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
- sk_sp<SkSurface> dstSurface =
- surfaceTextureRef->getTexture()->getOrCreateSurface(dstDataspace, grContext.get());
+ sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
@@ -807,7 +847,9 @@
// Layers have a local transform that should be applied to them
canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
- const auto bounds = getSkRect(layer->geometry.boundaries);
+ const auto [bounds, roundRectClip] =
+ getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop,
+ layer->geometry.roundedCornersRadius);
if (mBlurFilter && layerHasBlur(layer)) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
@@ -817,53 +859,81 @@
blurInput = activeSurface->makeImageSnapshot();
}
// rect to be blurred in the coordinate space of blurInput
- const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+ const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
- if (layer->backgroundBlurRadius > 0) {
- ATRACE_NAME("BackgroundBlur");
- auto blurredImage =
- mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
- blurInput, blurRect);
-
- cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
-
- mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage,
- blurInput);
+ // if the clip needs to be applied then apply it now and make sure
+ // it is restored before we attempt to draw any shadows.
+ SkAutoCanvasRestore acr(canvas, true);
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
}
- for (auto region : layer->blurRegions) {
- if (cachedBlurs[region.blurRadius] == nullptr) {
- ATRACE_NAME("BlurRegion");
- cachedBlurs[region.blurRadius] =
- mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+
+ // TODO(b/182216890): Filter out empty layers earlier
+ if (blurRect.width() > 0 && blurRect.height() > 0) {
+ if (layer->backgroundBlurRadius > 0) {
+ ATRACE_NAME("BackgroundBlur");
+ auto blurredImage =
+ mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
blurRect);
+
+ cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+
+ mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+ blurRect, blurredImage, blurInput);
}
- mBlurFilter->drawBlurRegion(canvas, region, blurRect,
- cachedBlurs[region.blurRadius], blurInput);
+ canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
+ for (auto region : layer->blurRegions) {
+ if (cachedBlurs[region.blurRadius] == nullptr) {
+ ATRACE_NAME("BlurRegion");
+ cachedBlurs[region.blurRadius] =
+ mBlurFilter->generate(grContext, region.blurRadius, blurInput,
+ blurRect);
+ }
+
+ mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
+ region.alpha, blurRect,
+ cachedBlurs[region.blurRadius], blurInput);
+ }
}
}
- // Shadows are assumed to live only on their own layer - it's not valid
- // to draw the boundary rectangles when there is already a caster shadow
- // TODO(b/175915334): consider relaxing this restriction to enable more flexible
- // composition - using a well-defined invalid color is long-term less error-prone.
if (layer->shadow.length > 0) {
- const auto rect = layer->geometry.roundedCornersRadius > 0
- ? getSkRect(layer->geometry.roundedCornersCrop)
- : bounds;
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
- drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
- continue;
+
+ SkRRect shadowBounds, shadowClip;
+ if (layer->geometry.boundaries == layer->shadow.boundaries) {
+ shadowBounds = bounds;
+ shadowClip = roundRectClip;
+ } else {
+ std::tie(shadowBounds, shadowClip) =
+ getBoundsAndClip(layer->shadow.boundaries,
+ layer->geometry.roundedCornersCrop,
+ layer->geometry.roundedCornersRadius);
+ }
+
+ // Technically, if bounds is a rect and roundRectClip is not empty,
+ // it means that the bounds and roundedCornersCrop were different
+ // enough that we should intersect them to find the proper shadow.
+ // In practice, this often happens when the two rectangles appear to
+ // not match due to rounding errors. Draw the rounded version, which
+ // looks more like the intent.
+ const auto& rrect =
+ shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
+ drawShadow(canvas, rrect, layer->shadow);
}
const bool requiresLinearEffect = layer->colorTransform != mat4() ||
(mUseColorManagement &&
- needsToneMapping(layer->sourceDataspace, display.outputDataspace));
+ needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
+ (display.sdrWhitePointNits > 0.f &&
+ display.sdrWhitePointNits != display.maxLuminance);
// quick abort from drawing the remaining portion of the layer
- if (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
- (!displayColorTransform || displayColorTransform->isAlphaUnchanged())) {
+ if (layer->skipContentDraw ||
+ (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
+ (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
continue;
}
@@ -889,18 +959,17 @@
// it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
// we didn't find anything in the cache then we intentionally did not cache this
// buffer's resources.
- imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(
- new AutoBackendTexture(grContext.get(),
- item.buffer->getBuffer()->toAHardwareBuffer(),
- false));
+ imageTextureRef = std::make_shared<
+ AutoBackendTexture::LocalRef>(grContext,
+ item.buffer->getBuffer()->toAHardwareBuffer(),
+ false);
}
sk_sp<SkImage> image =
- imageTextureRef->getTexture()->makeImage(layerDataspace,
- item.usePremultipliedAlpha
- ? kPremul_SkAlphaType
- : kUnpremul_SkAlphaType,
- grContext.get());
+ imageTextureRef->makeImage(layerDataspace,
+ item.usePremultipliedAlpha ? kPremul_SkAlphaType
+ : kUnpremul_SkAlphaType,
+ grContext);
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTansform was intended to be passed directly into a shader, so when
@@ -916,7 +985,7 @@
// The shader does not respect the translation, so we add it to the texture
// transform for the SkImage. This will make sure that the correct layer contents
// are drawn in the correct part of the screen.
- matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top);
+ matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
sk_sp<SkShader> shader;
@@ -978,11 +1047,15 @@
paint.setColorFilter(displayColorTransform);
- if (layer->geometry.roundedCornersRadius > 0) {
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
+
+ if (!bounds.isRect()) {
paint.setAntiAlias(true);
- canvas->drawRRect(getRoundedRect(layer), paint);
+ canvas->drawRRect(bounds, paint);
} else {
- canvas->drawRect(bounds, paint);
+ canvas->drawRect(bounds.rect(), paint);
}
if (kFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
@@ -1030,25 +1103,89 @@
return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
-inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
- const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
- const auto cornerRadius = layer->geometry.roundedCornersRadius;
- return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
-}
+inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
+ const FloatRect& cropRect,
+ const float cornerRadius) {
+ const SkRect bounds = getSkRect(boundsRect);
+ const SkRect crop = getSkRect(cropRect);
-inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) {
- const auto rect = getSkRect(layer->geometry.boundaries);
- const auto cornersRadius = layer->geometry.roundedCornersRadius;
- return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius),
- .cornerRadiusTL = cornersRadius,
- .cornerRadiusTR = cornersRadius,
- .cornerRadiusBL = cornersRadius,
- .cornerRadiusBR = cornersRadius,
- .alpha = 1,
- .left = static_cast<int>(rect.fLeft),
- .top = static_cast<int>(rect.fTop),
- .right = static_cast<int>(rect.fRight),
- .bottom = static_cast<int>(rect.fBottom)};
+ SkRRect clip;
+ if (cornerRadius > 0) {
+ // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
+ if (bounds == crop || crop.isEmpty()) {
+ return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+ }
+
+ // This makes an effort to speed up common, simple bounds + clip combinations by
+ // converting them to a single RRect draw. It is possible there are other cases
+ // that can be converted.
+ if (crop.contains(bounds)) {
+ bool intersectionIsRoundRect = true;
+ // check each cropped corner to ensure that it exactly matches the crop or is full
+ SkVector radii[4];
+
+ const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+
+ const bool leftEqual = bounds.fLeft == crop.fLeft;
+ const bool topEqual = bounds.fTop == crop.fTop;
+ const bool rightEqual = bounds.fRight == crop.fRight;
+ const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+ // compute the UpperLeft corner radius
+ if (leftEqual && topEqual) {
+ radii[0].set(cornerRadius, cornerRadius);
+ } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
+ insetCrop.contains(bounds.fLeft, bounds.fTop)) {
+ radii[0].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the UpperRight corner radius
+ if (rightEqual && topEqual) {
+ radii[1].set(cornerRadius, cornerRadius);
+ } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fRight <= insetCrop.fRight) ||
+ insetCrop.contains(bounds.fRight, bounds.fTop)) {
+ radii[1].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomRight corner radius
+ if (rightEqual && bottomEqual) {
+ radii[2].set(cornerRadius, cornerRadius);
+ } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
+ insetCrop.contains(bounds.fRight, bounds.fBottom)) {
+ radii[2].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomLeft corner radius
+ if (leftEqual && bottomEqual) {
+ radii[3].set(cornerRadius, cornerRadius);
+ } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
+ insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
+ radii[3].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+
+ if (intersectionIsRoundRect) {
+ SkRRect intersectionBounds;
+ intersectionBounds.setRectRadii(bounds, radii);
+ return {intersectionBounds, clip};
+ }
+ }
+
+ // we didn't it any of our fast paths so set the clip to the cropRect
+ clip.setRectXY(crop, cornerRadius, cornerRadius);
+ }
+
+ // if we hit this point then we either don't have rounded corners or we are going to rely
+ // on the clip to round the corners for us
+ return {SkRRect::MakeRect(bounds), clip};
}
inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
@@ -1078,17 +1215,14 @@
return mGrContext->maxRenderTargetSize();
}
-void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& settings) {
ATRACE_CALL();
const float casterZ = settings.length / 2.0f;
- const auto shadowShape = cornerRadius > 0
- ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius))
- : SkPath::Rect(casterRect);
const auto flags =
settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
- SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ),
+ SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
getSkPoint3(settings.lightPos), settings.lightRadius,
getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
flags);
@@ -1220,13 +1354,11 @@
const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
// start by resizing the current context
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// if it is possible to switch contexts then we will resize the other context
if (useProtectedContext(!mInProtectedContext)) {
- grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// reset back to the initial context that was active when this method was called
useProtectedContext(!mInProtectedContext);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index e71c560..4265c08 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -39,6 +39,7 @@
#include "debug/SkiaCapture.h"
#include "filters/BlurFilter.h"
#include "filters/LinearEffect.h"
+#include "filters/StretchShaderFactory.h"
namespace android {
namespace renderengine {
@@ -87,21 +88,23 @@
int hwcFormat, Protection protection);
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
- inline SkRRect getRoundedRect(const LayerSettings* layer);
- inline BlurRegion getBlurRegion(const LayerSettings* layer);
+ inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
+ const FloatRect& crop, float cornerRadius);
inline bool layerHasBlur(const LayerSettings* layer);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
inline SkPoint3 getSkPoint3(const vec3& vector);
+ inline GrDirectContext* getActiveGrContext() const;
base::unique_fd flush();
bool waitFence(base::unique_fd fenceFd);
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
- void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+ 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,
+ sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader,
+ const LayerSettings* layer,
const DisplaySettings& display,
bool undoPremultipliedAlpha,
bool requiresLinearEffect);
@@ -116,14 +119,21 @@
const PixelFormat mDefaultPixelFormat;
const bool mUseColorManagement;
+ // Identifier used or various mappings of layers to various
+ // textures or shaders
+ using GraphicBufferId = uint64_t;
+
// Number of external holders of ExternalTexture references, per GraphicBuffer ID.
- std::unordered_map<uint64_t, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
- // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
- std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
GUARDED_BY(mRenderingMutex);
- std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
+ // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+ std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ GUARDED_BY(mRenderingMutex);
+ std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>>
mProtectedTextureCache GUARDED_BY(mRenderingMutex);
std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+
+ StretchShaderFactory mStretchShaderFactory;
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh
index bc406d9..25c8cef 100755
--- a/libs/renderengine/skia/debug/record.sh
+++ b/libs/renderengine/skia/debug/record.sh
@@ -31,7 +31,7 @@
# give the device time to both record, and starting writing the file.
# Total time needed to write the file depends on how much data was recorded.
# the loop at the end waits for this.
-sleep $(($1 / 1000 + 2));
+sleep $(($1 / 1000 + 4));
# There is no guarantee that at least one frame passed through renderengine during that time
# but as far as I know it always at least writes a 0-byte file with a new name, unless it crashes
@@ -54,7 +54,7 @@
mskp_size=$(adb_filesize "/data/user/$name")
if [[ $mskp_size = "0" ]]; then
- echo "Empty file, probably no RenderEngine activity during recording period."
+ echo "File opened, but remains empty after recording period + wait. Either there was no RenderEngine activity during recording period, or recording process is still working. Check /data/user/$name manually later."
exit 1
fi
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 6dd4161..4ad6e94 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -53,7 +53,7 @@
}
)");
- auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
if (!blurEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
}
@@ -65,11 +65,11 @@
uniform float mixFactor;
half4 main(float2 xy) {
- return half4(mix(sample(originalInput), sample(blurredInput), mixFactor));
+ return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
}
)");
- auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString);
+ auto [mixEffect, mixError] = SkRuntimeEffect::MakeForShader(mixString);
if (!mixEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
}
@@ -86,8 +86,8 @@
float radiusByPasses = tmpRadius / (float)numberOfPasses;
// create blur surface with the bit depth and colorspace of the original surface
- SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale,
- blurRect.height() * kInputScale);
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
const float stepX = radiusByPasses;
const float stepY = radiusByPasses;
@@ -105,7 +105,7 @@
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 - 1, blurRect.height() * kInputScale - 1};
+ SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
@@ -116,7 +116,7 @@
tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1};
+ SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
}
@@ -139,23 +139,21 @@
return matrix;
}
-void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
sk_sp<SkImage> input) {
ATRACE_CALL();
SkPaint paint;
- paint.setAlphaf(effectRegion.alpha);
- if (effectRegion.alpha == 1.0f) {
- paint.setBlendMode(SkBlendMode::kSrc);
- }
+ paint.setAlphaf(blurAlpha);
const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < kMaxCrossFadeRadius) {
// 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;
@@ -168,30 +166,21 @@
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
paint.setShader(blurShader);
}
- // TODO we should AA at least the drawRoundRect which would mean no SRC blending
- // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
- auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
- effectRegion.bottom);
-
- if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
- effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
- const SkVector radii[4] =
- {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
- SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
- SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
- SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
- SkRRect roundedRect;
- roundedRect.setRectRadii(rect, radii);
- canvas->drawRRect(roundedRect, paint);
+ if (effectRegion.isRect()) {
+ if (blurAlpha == 1.0f) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+ canvas->drawRect(effectRegion.rect(), paint);
} else {
- canvas->drawRect(rect, paint);
+ paint.setAntiAlias(true);
+ canvas->drawRRect(effectRegion, paint);
}
}
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 731ba11..a8e6dd7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -20,7 +20,6 @@
#include <SkImage.h>
#include <SkRuntimeEffect.h>
#include <SkSurface.h>
-#include <ui/BlurRegion.h>
using namespace std;
@@ -52,8 +51,19 @@
sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
- void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
- sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
+ /**
+ * Draw the blurred content (from the generate method) into the canvas.
+ * @param canvas is the destination/output for the blur
+ * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage
+ * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect
+ * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn
+ * @param blurRect bounds of the blurredImage translated into canvas coordinates
+ * @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);
private:
sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 8e8e42e..9b044e1 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -127,7 +127,7 @@
default:
shader.append(R"(
float3 ScaleLuminance(float3 xyz) {
- return xyz * in_displayMaxLuminance;
+ return xyz * in_inputMaxLuminance;
}
)");
break;
@@ -438,7 +438,7 @@
generateOETF(linearEffect.outputDataspace, shaderString);
generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
- auto [shader, error] = SkRuntimeEffect::Make(shaderString);
+ auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
if (!shader) {
LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
}
@@ -448,7 +448,7 @@
sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
- float maxMasteringLuminance, float maxContentLuminance) {
+ float maxLuminance) {
ATRACE_CALL();
SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
@@ -467,8 +467,10 @@
}
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") =
- std::min(maxMasteringLuminance, maxContentLuminance);
+ 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 20b8338..14a3b61 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -90,15 +90,13 @@
// matrix transforming from linear XYZ to linear RGB immediately before OETF.
// We also provide additional HDR metadata upon creating the shader:
// * The max display luminance is the max luminance of the physical display in nits
-// * The max mastering luminance is provided as the max luminance from the SMPTE 2086
-// standard.
-// * The max content luminance is provided as the max light level from the CTA 861.3
-// standard.
+// * 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,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
- float maxMasteringLuminance, float maxContentLuminance);
+ float maxLuminance);
} // namespace skia
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
new file mode 100644
index 0000000..4ac5c40
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "StretchShaderFactory.h"
+#include <SkImageFilter.h>
+#include <SkRefCnt.h>
+#include <SkRuntimeEffect.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include "log/log.h"
+#include <memory>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString stretchShader = SkString(R"(
+ uniform shader uContentTexture;
+
+ // multiplier to apply to scale effect
+ uniform float uMaxStretchIntensity;
+
+ // Maximum percentage to stretch beyond bounds of target
+ uniform float uStretchAffectedDistX;
+ uniform float uStretchAffectedDistY;
+
+ // Distance stretched as a function of the normalized overscroll times
+ // scale intensity
+ uniform float uDistanceStretchedX;
+ uniform float uDistanceStretchedY;
+ uniform float uInverseDistanceStretchedX;
+ uniform float uInverseDistanceStretchedY;
+ uniform float uDistDiffX;
+
+ // Difference between the peak stretch amount and overscroll amount normalized
+ uniform float uDistDiffY;
+
+ // Horizontal offset represented as a ratio of pixels divided by the target width
+ uniform float uScrollX;
+ // Vertical offset represented as a ratio of pixels divided by the target height
+ uniform float uScrollY;
+
+ // Normalized overscroll amount in the horizontal direction
+ uniform float uOverscrollX;
+
+ // Normalized overscroll amount in the vertical direction
+ uniform float uOverscrollY;
+ uniform float viewportWidth; // target height in pixels
+ uniform float viewportHeight; // target width in pixels
+
+ // uInterpolationStrength is the intensity of the interpolation.
+ // if uInterpolationStrength is 0, then the stretch is constant for all the
+ // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch intensity
+ // is interpolated based on the pixel position in the uStretchAffectedDist area;
+ // The closer we are from the scroll anchor point, the more it stretches,
+ // and the other way around.
+ uniform float uInterpolationStrength;
+
+ float easeIn(float t, float d) {
+ return t * d;
+ }
+
+ float computeOverscrollStart(
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float interpolationStrength
+ ) {
+ float offsetPos = uStretchAffectedDist - inPos;
+ float posBasedVariation = mix(
+ 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ float stretchIntensity = overscroll * posBasedVariation;
+ return distanceStretched - (offsetPos / (1. + stretchIntensity));
+ }
+
+ float computeOverscrollEnd(
+ float inPos,
+ float overscroll,
+ float reverseStretchDist,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float interpolationStrength
+ ) {
+ float offsetPos = inPos - reverseStretchDist;
+ float posBasedVariation = mix(
+ 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ float stretchIntensity = (-overscroll) * posBasedVariation;
+ return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
+ }
+
+ // Prefer usage of return values over out parameters as it enables
+ // SKSL to properly inline method calls and works around potential GPU
+ // driver issues on Wembly. See b/182566543 for details
+ float computeOverscroll(
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float distanceDiff,
+ float interpolationStrength
+ ) {
+ float outPos = inPos;
+ // overscroll is provided via uniform so there is no concern
+ // for potential incoherent branches
+ if (overscroll > 0) {
+ if (inPos <= uStretchAffectedDist) {
+ outPos = computeOverscrollStart(
+ inPos,
+ overscroll,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength
+ );
+ } else if (inPos >= distanceStretched) {
+ outPos = distanceDiff + inPos;
+ }
+ }
+ if (overscroll < 0) {
+ float stretchAffectedDist = 1. - uStretchAffectedDist;
+ if (inPos >= stretchAffectedDist) {
+ outPos = computeOverscrollEnd(
+ inPos,
+ overscroll,
+ stretchAffectedDist,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength
+ );
+ } else if (inPos < stretchAffectedDist) {
+ outPos = -distanceDiff + inPos;
+ }
+ }
+ return outPos;
+ }
+
+ vec4 main(vec2 coord) {
+ // Normalize SKSL pixel coordinate into a unit vector
+ float inU = coord.x / viewportWidth;
+ float inV = coord.y / viewportHeight;
+ float outU;
+ float outV;
+ float stretchIntensity;
+ // Add the normalized scroll position within scrolling list
+ inU += uScrollX;
+ inV += uScrollY;
+ outU = inU;
+ outV = inV;
+ outU = computeOverscroll(
+ inU,
+ uOverscrollX,
+ uStretchAffectedDistX,
+ uInverseDistanceStretchedX,
+ uDistanceStretchedX,
+ uDistDiffX,
+ uInterpolationStrength
+ );
+ outV = computeOverscroll(
+ inV,
+ uOverscrollY,
+ uStretchAffectedDistY,
+ uInverseDistanceStretchedY,
+ uDistanceStretchedY,
+ uDistDiffY,
+ uInterpolationStrength
+ );
+ coord.x = (outU - uScrollX) * viewportWidth;
+ coord.y = (outV - uScrollY) * viewportHeight;
+ return sample(uContentTexture, coord);
+ })");
+
+const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+
+sk_sp<SkShader> StretchShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
+ const StretchEffect& stretchEffect) {
+ if (!stretchEffect.hasEffect()) {
+ return nullptr;
+ }
+
+ float viewportWidth = stretchEffect.width;
+ float viewportHeight = stretchEffect.height;
+ float normOverScrollDistX = stretchEffect.vectorX;
+ float normOverScrollDistY = stretchEffect.vectorY;
+ float distanceStretchedX =
+ StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX));
+ float distanceStretchedY =
+ StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistY));
+ float inverseDistanceStretchedX =
+ 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float inverseDistanceStretchedY =
+ 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float diffX =
+ distanceStretchedX - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float diffY =
+ distanceStretchedY - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ auto& srcBounds = stretchEffect.mappedChildBounds;
+ float normalizedScrollX = srcBounds.left / viewportWidth;
+ float normalizedScrollY = srcBounds.top / viewportHeight;
+
+ if (mBuilder == nullptr) {
+ const static SkRuntimeEffect::Result instance =
+ SkRuntimeEffect::MakeForShader(stretchShader);
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+ }
+
+ mBuilder->child("uContentTexture") = inputShader;
+ mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
+ mBuilder->uniform("uStretchAffectedDistX").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+ mBuilder->uniform("uStretchAffectedDistY").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+ mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
+ mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
+ mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1);
+ mBuilder->uniform("uInverseDistanceStretchedY").set(&inverseDistanceStretchedY, 1);
+ mBuilder->uniform("uDistDiffX").set(&diffX, 1);
+ mBuilder->uniform("uDistDiffY").set(&diffY, 1);
+ mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
+ mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
+ mBuilder->uniform("uScrollX").set(&normalizedScrollX, 1);
+ mBuilder->uniform("uScrollY").set(&normalizedScrollY, 1);
+ mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
+ mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
+
+ return mBuilder->makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.h b/libs/renderengine/skia/filters/StretchShaderFactory.h
new file mode 100644
index 0000000..9c3ab7c
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <ui/StretchEffect.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+class StretchShaderFactory {
+public:
+ sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader,
+ const StretchEffect& stretchEffect);
+
+private:
+ std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d63c88b..1ca7a16 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -263,6 +263,11 @@
}
}
+ void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance);
+ }
+
void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
uint8_t tolerance = 0) {
auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
@@ -487,6 +492,9 @@
void fillBufferAndBlurBackground();
template <typename SourceVariant>
+ void fillSmallLayerAndBlurBackground();
+
+ template <typename SourceVariant>
void overlayCorners();
void fillRedBufferTextureTransform();
@@ -966,11 +974,44 @@
if (mRE->supportsBackgroundBlur()) {
// blurred color (downsampling should result in the center color being close to 128)
expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255,
- 10 /* tolerance */);
+ 50 /* tolerance */);
}
}
template <typename SourceVariant>
+void RenderEngineTest::fillSmallLayerAndBlurBackground() {
+ auto blurRadius = 50;
+ renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings backgroundLayer;
+ backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(backgroundLayer, 1.0f, 0.0f, 0.0f, this);
+ backgroundLayer.alpha = 1.0f;
+ layers.push_back(&backgroundLayer);
+
+ renderengine::LayerSettings blurLayer;
+ blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ blurLayer.geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f);
+ blurLayer.backgroundBlurRadius = blurRadius;
+ SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
+ blurLayer.alpha = 0;
+ layers.push_back(&blurLayer);
+
+ invokeDraw(settings, layers);
+
+ // Give a generous tolerance - the blur rectangle is very small and this test is
+ // mainly concerned with ensuring that there's no device failure.
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255,
+ 40 /* tolerance */);
+}
+
+template <typename SourceVariant>
void RenderEngineTest::overlayCorners() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -1224,6 +1265,7 @@
renderengine::LayerSettings shadowLayer;
shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
shadowLayer.geometry.boundaries = castingBounds;
+ shadowLayer.skipContentDraw = true;
shadowLayer.alpha = 1.0f;
ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
shadowLayer.shadow = shadow;
@@ -1407,6 +1449,11 @@
fillBufferAndBlurBackground<ColorSourceVariant>();
}
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) {
+ initializeRenderEngine();
+ fillSmallLayerAndBlurBackground<ColorSourceVariant>();
+}
+
TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
initializeRenderEngine();
overlayCorners<ColorSourceVariant>();
@@ -1482,6 +1529,11 @@
fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) {
+ initializeRenderEngine();
+ fillSmallLayerAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
initializeRenderEngine();
overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
@@ -1557,6 +1609,11 @@
fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) {
+ initializeRenderEngine();
+ fillSmallLayerAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
initializeRenderEngine();
overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
@@ -1836,6 +1893,51 @@
0, 255, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(&redLayer);
+
+ // Green layer with 1/2 size with parent crop rect.
+ renderengine::LayerSettings greenLayer = redLayer;
+ greenLayer.geometry.boundaries =
+ FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
+ greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
+
+ layers.push_back(&greenLayer);
+
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+ // top middle should be green and the bottom middle red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+
+ // the bottom edge of the green layer should not be rounded
+ expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index c87a836..9009ce4 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -101,6 +101,7 @@
}
void RenderEngineThreaded::primeCache() {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
@@ -131,6 +132,7 @@
}
void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+ ATRACE_CALL();
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
@@ -146,6 +148,7 @@
}
void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+ ATRACE_CALL();
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
@@ -162,6 +165,7 @@
void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
@@ -175,6 +179,7 @@
}
void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
@@ -247,6 +252,7 @@
const bool useFramebufferCache,
base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
+ ATRACE_CALL();
std::promise<status_t> resultPromise;
std::future<status_t> resultFuture = resultPromise.get_future();
{
@@ -264,6 +270,7 @@
}
void RenderEngineThreaded::cleanFramebufferCache() {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 240738d..0a49008 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -231,6 +231,10 @@
mFlags |= SENSOR_FLAG_WAKE_UP;
}
break;
+ case SENSOR_TYPE_DEVICE_ORIENTATION:
+ mStringType = SENSOR_STRING_TYPE_DEVICE_ORIENTATION;
+ mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+ break;
case SENSOR_TYPE_DYNAMIC_SENSOR_META:
mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index 4714469..ef3ceda 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -55,6 +55,22 @@
return service;
}
+bool SensorPrivacyManager::supportsSensorToggle(int sensor) {
+ if (mSupportedCache.find(sensor) == mSupportedCache.end()) {
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ bool result;
+ service->supportsSensorToggle(sensor, &result);
+ mSupportedCache[sensor] = result;
+ return result;
+ }
+ // if the SensorPrivacyManager is not available then assume sensor privacy feature isn't
+ // supported
+ return false;
+ }
+ return mSupportedCache[sensor];
+}
+
void SensorPrivacyManager::addSensorPrivacyListener(
const sp<hardware::ISensorPrivacyListener>& listener)
{
@@ -84,6 +100,15 @@
}
}
+void SensorPrivacyManager::removeIndividualSensorPrivacyListener(int sensor,
+ const sp<hardware::ISensorPrivacyListener>& listener)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ service->removeIndividualSensorPrivacyListener(sensor, listener);
+ }
+}
+
bool SensorPrivacyManager::isSensorPrivacyEnabled()
{
sp<hardware::ISensorPrivacyManager> service = getService();
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index 629b8c2..f91f5b9 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -20,12 +20,16 @@
/** @hide */
interface ISensorPrivacyManager {
+ boolean supportsSensorToggle(int sensor);
+
void addSensorPrivacyListener(in ISensorPrivacyListener listener);
void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener);
void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
+ void removeIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener);
+
boolean isSensorPrivacyEnabled();
boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 12778e1..af699d0 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -22,6 +22,8 @@
#include <utils/threads.h>
+#include <unordered_map>
+
// ---------------------------------------------------------------------------
namespace android {
@@ -35,10 +37,13 @@
SensorPrivacyManager();
+ bool supportsSensorToggle(int sensor);
void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
status_t addIndividualSensorPrivacyListener(int userId, int sensor,
const sp<hardware::ISensorPrivacyListener>& listener);
void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+ void removeIndividualSensorPrivacyListener(int sensor,
+ const sp<hardware::ISensorPrivacyListener>& listener);
bool isSensorPrivacyEnabled();
bool isIndividualSensorPrivacyEnabled(int userId, int sensor);
status_t isIndividualSensorPrivacyEnabled(int userId, int sensor, bool &result);
@@ -50,6 +55,8 @@
Mutex mLock;
sp<hardware::ISensorPrivacyManager> mService;
sp<hardware::ISensorPrivacyManager> getService();
+
+ std::unordered_map<int, bool> mSupportedCache;
};
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 636fbde..9dc9beb 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -36,6 +36,7 @@
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
+using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace;
using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
@@ -597,7 +598,7 @@
if (!outDataspace) {
return BAD_VALUE;
}
- aidl::android::hardware::graphics::common::Dataspace dataspace;
+ AidlDataspace dataspace;
status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
&dataspace);
if (error) {
@@ -841,6 +842,7 @@
uint32_t pixelFormatFourCC;
uint64_t pixelFormatModifier;
uint64_t usage;
+ AidlDataspace dataspace;
uint64_t allocationSize;
uint64_t protectedContent;
ExtendableType compression;
@@ -892,6 +894,11 @@
if (error != NO_ERROR) {
return error;
}
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::DATASPACE,
+ gralloc4::decodeDataspace, &dataspace);
+ if (error != NO_ERROR) {
+ return error;
+ }
error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
gralloc4::decodeAllocationSize, &allocationSize);
if (error != NO_ERROR) {
@@ -932,6 +939,7 @@
<< "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
<< std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
<< ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+ << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace)
<< ", compressed: ";
if (less) {
diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h
index 69a586e..a9ca369 100644
--- a/libs/ui/include/ui/BlurRegion.h
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -20,6 +20,8 @@
#include <iosfwd>
#include <iostream>
+#include <math/HashCombine.h>
+
namespace android {
struct BlurRegion {
@@ -33,6 +35,16 @@
int top;
int right;
int bottom;
+
+ inline bool operator==(const BlurRegion& other) const {
+ return blurRadius == other.blurRadius && cornerRadiusTL == other.cornerRadiusTL &&
+ cornerRadiusTR == other.cornerRadiusTR && cornerRadiusBL == other.cornerRadiusBL &&
+ cornerRadiusBR == other.cornerRadiusBR && alpha == other.alpha &&
+ left == other.left && top == other.top && right == other.right &&
+ bottom == other.bottom;
+ }
+
+ inline bool operator!=(const BlurRegion& other) const { return !(*this == other); }
};
static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) {
@@ -50,4 +62,15 @@
*os << "\n}";
}
-} // namespace android
\ No newline at end of file
+} // namespace android
+
+namespace std {
+template <>
+struct hash<android::BlurRegion> {
+ size_t operator()(const android::BlurRegion& region) const {
+ return android::hashCombine(region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR,
+ region.cornerRadiusBL, region.cornerRadiusBR, region.alpha,
+ region.left, region.top, region.right, region.bottom);
+ }
+};
+} // namespace std
\ No newline at end of file
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
index 0803df3..cf08acb 100644
--- a/libs/ui/include/ui/StretchEffect.h
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -25,31 +25,49 @@
namespace android {
struct StretchEffect : public LightFlattenablePod<StretchEffect> {
- FloatRect area = {0, 0, 0, 0};
- float vectorX = 0;
- float vectorY = 0;
- float maxAmount = 0;
+ constexpr static const float CONTENT_DISTANCE_STRETCHED = 1.f;
- bool operator==(const StretchEffect& other) const {
- return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY &&
- maxAmount == other.maxAmount;
+ float width = 0;
+ float height = 0;
+ float vectorX = 0;
+ float vectorY = 0;
+ float maxAmountX = 0;
+ float maxAmountY = 0;
+ FloatRect mappedChildBounds = {0, 0, 0, 0};
+
+ bool operator==(const StretchEffect& other) const {
+ return width == other.width && height == other.height &&
+ vectorX == other.vectorX &&
+ vectorY == other.vectorY &&
+ maxAmountX == other.maxAmountX &&
+ maxAmountY == other.maxAmountY &&
+ mappedChildBounds == other.mappedChildBounds;
+ }
+
+ static bool isZero(float value) {
+ constexpr float NON_ZERO_EPSILON = 0.001f;
+ return fabsf(value) <= NON_ZERO_EPSILON;
+ }
+
+ bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
+
+ bool hasEffect() const { return !isNoOp(); }
+
+ void sanitize() {
+ // If the area is empty, or the max amount is zero, then reset back to defaults
+ if (width == 0.f || height == 0.f || isZero(maxAmountX) ||
+ isZero(maxAmountY)) {
+ *this = StretchEffect{};
}
+ }
- static bool isZero(float value) {
- constexpr float NON_ZERO_EPSILON = 0.001f;
- return fabsf(value) <= NON_ZERO_EPSILON;
- }
+ float getStretchWidthMultiplier() const {
+ return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorX));
+ }
- bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
-
- bool hasEffect() const { return !isNoOp(); }
-
- void sanitize() {
- // If the area is empty, or the max amount is zero, then reset back to defaults
- if (area.isEmpty() || isZero(maxAmount)) {
- *this = StretchEffect{};
- }
- }
+ float getStretchHeightMultiplier() const {
+ return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorY));
+ }
};
static_assert(std::is_trivially_copyable<StretchEffect>::value,
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 3878cb1..b15694b 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -69,11 +69,9 @@
host_supported: true,
vendor_available: true,
export_include_dirs: ["include"],
-}
-
-llndk_headers {
- name: "gl_llndk_headers",
- export_include_dirs: ["include"],
+ llndk: {
+ llndk_headers: true,
+ },
}
subdirs = [
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 7861d62..c9fce8a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -137,7 +137,12 @@
cc_library_shared {
name: "libEGL",
defaults: ["egl_libs_defaults"],
- llndk_stubs: "libEGL.llndk",
+ llndk: {
+ symbol_file: "libEGL.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: [
"EGL/egl_tls.cpp",
"EGL/egl_cache.cpp",
@@ -203,7 +208,12 @@
cc_library_shared {
name: "libGLESv1_CM",
defaults: ["gles_libs_defaults"],
- llndk_stubs: "libGLESv1_CM.llndk",
+ llndk: {
+ symbol_file: "libGLESv1_CM.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: ["GLES_CM/gl.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv1\""],
version_script: "libGLESv1_CM.map.txt",
@@ -215,7 +225,12 @@
cc_library_shared {
name: "libGLESv2",
defaults: ["gles_libs_defaults"],
- llndk_stubs: "libGLESv2.llndk",
+ llndk: {
+ symbol_file: "libGLESv2.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv2\""],
@@ -230,31 +245,12 @@
cc_library_shared {
name: "libGLESv3",
defaults: ["gles_libs_defaults"],
- llndk_stubs: "libGLESv3.llndk",
+ llndk: {
+ symbol_file: "libGLESv3.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv3\""],
}
-
-llndk_library {
- name: "libEGL.llndk",
- symbol_file: "libEGL.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
- name: "libGLESv1_CM.llndk",
- symbol_file: "libGLESv1_CM.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
- name: "libGLESv2.llndk",
- symbol_file: "libGLESv2.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
- name: "libGLESv3.llndk",
- symbol_file: "libGLESv3.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 9b98a17..6612a93 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -63,11 +63,16 @@
"libcutils",
"libhidlbase",
"libinput",
+ "libkll",
"liblog",
+ "libprotobuf-cpp-lite",
"libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libutils",
"libui",
"lib-platform-compat-native-api",
+ "server_configurable_flags",
],
static_libs: [
"libattestation",
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 8073a93..dc0e60c 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -38,6 +38,14 @@
"include-filter": "android.security.cts.MotionEventTest"
}
]
+ },
+ {
+ "name": "CtsSecurityBulletinHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.security.cts.Poc19_03#testPocBug_115739809"
+ }
+ ]
}
]
}
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index ea37f4d..902bd0d 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -12,7 +12,10 @@
srcs: [
"InputDispatcher_benchmarks.cpp",
],
- defaults: ["inputflinger_defaults"],
+ defaults: [
+ "inputflinger_defaults",
+ "libinputdispatcher_defaults",
+ ],
shared_libs: [
"libbase",
"libbinder",
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 7bd0c6b..bc77b8a 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -16,9 +16,11 @@
#include <benchmark/benchmark.h>
+#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
#include "../dispatcher/InputDispatcher.h"
+using android::os::IInputConstants;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
@@ -226,13 +228,14 @@
ui::Transform identityTransform;
MotionEvent event;
- event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ event.initialize(IInputConstants::INVALID_INPUT_EVENT_ID, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
identityTransform, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
return event;
}
@@ -251,9 +254,9 @@
const nsecs_t currentTime = now();
// Define a valid motion event.
- NotifyMotionArgs args(/* id */ 0, currentTime, currentTime, DEVICE_ID,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER,
- AMOTION_EVENT_ACTION_DOWN,
+ NotifyMotionArgs args(IInputConstants::INVALID_INPUT_EVENT_ID, currentTime, currentTime,
+ DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
pointerProperties, pointerCoords,
@@ -282,14 +285,12 @@
for (auto _ : state) {
// Send ACTION_DOWN
motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
- motionArgs.id = 0;
motionArgs.downTime = now();
motionArgs.eventTime = motionArgs.downTime;
dispatcher->notifyMotion(&motionArgs);
// Send ACTION_UP
motionArgs.action = AMOTION_EVENT_ACTION_UP;
- motionArgs.id = 1;
motionArgs.eventTime = now();
dispatcher->notifyMotion(&motionArgs);
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 9750ef9..1b3888b 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -38,8 +38,11 @@
"InjectionState.cpp",
"InputDispatcher.cpp",
"InputDispatcherFactory.cpp",
+ "InputEventTimeline.cpp",
"InputState.cpp",
"InputTarget.cpp",
+ "LatencyAggregator.cpp",
+ "LatencyTracker.cpp",
"Monitor.cpp",
"TouchState.cpp",
"DragState.cpp",
@@ -54,11 +57,16 @@
"libcrypto",
"libcutils",
"libinput",
+ "libkll",
"liblog",
+ "libprotobuf-cpp-lite",
"libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libui",
"libutils",
"lib-platform-compat-native-api",
+ "server_configurable_flags",
],
static_libs: [
"libattestation",
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 5270b8a..881024f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -296,12 +296,13 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor)
+ ui::Transform transform, float globalScaleFactor, int2 displaySize)
: seq(nextSeq()),
eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
transform(transform),
globalScaleFactor(globalScaleFactor),
+ displaySize(displaySize),
deliveryTime(0),
resolvedAction(0),
resolvedFlags(0) {}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index f3ef64b..ebbd8e9 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -79,7 +79,7 @@
explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
std::string getDescription() const override;
- virtual ~ConfigurationChangedEntry();
+ ~ConfigurationChangedEntry() override;
};
struct DeviceResetEntry : EventEntry {
@@ -88,7 +88,7 @@
DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
std::string getDescription() const override;
- virtual ~DeviceResetEntry();
+ ~DeviceResetEntry() override;
};
struct FocusEntry : EventEntry {
@@ -100,7 +100,7 @@
const std::string& reason);
std::string getDescription() const override;
- virtual ~FocusEntry();
+ ~FocusEntry() override;
};
struct PointerCaptureChangedEntry : EventEntry {
@@ -109,7 +109,7 @@
PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
std::string getDescription() const override;
- virtual ~PointerCaptureChangedEntry();
+ ~PointerCaptureChangedEntry() override;
};
struct DragEntry : EventEntry {
@@ -153,7 +153,7 @@
std::string getDescription() const override;
void recycle();
- virtual ~KeyEntry();
+ ~KeyEntry() override;
};
struct MotionEntry : EventEntry {
@@ -204,7 +204,7 @@
std::vector<float> values);
std::string getDescription() const override;
- virtual ~SensorEntry();
+ ~SensorEntry() override;
};
// Tracks the progress of dispatching a particular event to a particular connection.
@@ -215,6 +215,7 @@
int32_t targetFlags;
ui::Transform transform;
float globalScaleFactor;
+ int2 displaySize;
// Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
// and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
@@ -227,7 +228,7 @@
int32_t resolvedFlags;
DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor);
+ ui::Transform transform, float globalScaleFactor, int2 displaySize);
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 16abd48..7e069c8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -59,7 +59,6 @@
#include <log/log.h>
#include <log/log_event_list.h>
#include <powermanager/PowerManager.h>
-#include <statslog.h>
#include <unistd.h>
#include <utils/Trace.h>
@@ -291,27 +290,6 @@
return it != map.end() ? it->second : V{};
}
-/**
- * Find the entry in std::unordered_map by value, and remove it.
- * If more than one entry has the same value, then all matching
- * key-value pairs will be removed.
- *
- * Return true if at least one value has been removed.
- */
-template <typename K, typename V>
-static bool removeByValue(std::unordered_map<K, V>& map, const V& value) {
- bool removed = false;
- for (auto it = map.begin(); it != map.end();) {
- if (it->second == value) {
- it = map.erase(it);
- removed = true;
- } else {
- it++;
- }
- }
- return removed;
-}
-
static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
if (first == second) {
return true;
@@ -347,14 +325,16 @@
// values do not represent on-screen coordinates, so they should not have any window
// transformations applied to them.
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
- 1.0f /*globalScaleFactor*/);
+ 1.0f /*globalScaleFactor*/,
+ inputTarget.displaySize);
}
}
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
- inputTarget.globalScaleFactor);
+ inputTarget.globalScaleFactor,
+ inputTarget.displaySize);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -405,7 +385,8 @@
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
- firstPointerTransform, inputTarget.globalScaleFactor);
+ firstPointerTransform, inputTarget.globalScaleFactor,
+ inputTarget.displaySize);
return dispatchEntry;
}
@@ -473,6 +454,56 @@
return std::nullopt;
}
+static bool shouldReportMetricsForConnection(const Connection& connection) {
+ // Do not keep track of gesture monitors. They receive every event and would disproportionately
+ // affect the statistics.
+ if (connection.monitor) {
+ return false;
+ }
+ // If the connection is experiencing ANR, let's skip it. We have separate ANR metrics
+ if (!connection.responsive) {
+ return false;
+ }
+ return true;
+}
+
+static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry,
+ const Connection& connection) {
+ const EventEntry& eventEntry = *dispatchEntry.eventEntry;
+ const int32_t& inputEventId = eventEntry.id;
+ if (inputEventId != dispatchEntry.resolvedEventId) {
+ // Event was transmuted
+ return false;
+ }
+ if (inputEventId == android::os::IInputConstants::INVALID_INPUT_EVENT_ID) {
+ return false;
+ }
+ // Only track latency for events that originated from hardware
+ if (eventEntry.isSynthesized()) {
+ return false;
+ }
+ const EventEntry::Type& inputEventEntryType = eventEntry.type;
+ if (inputEventEntryType == EventEntry::Type::KEY) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+ if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
+ return false;
+ }
+ } else if (inputEventEntryType == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+ if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL ||
+ motionEntry.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ return false;
+ }
+ } else {
+ // Not a key or a motion
+ return false;
+ }
+ if (!shouldReportMetricsForConnection(connection)) {
+ return false;
+ }
+ return true;
+}
+
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -494,6 +525,8 @@
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mFocusedWindowRequestedPointerCapture(false),
mWindowTokenWithPointerCapture(nullptr),
+ mLatencyAggregator(),
+ mLatencyTracker(&mLatencyAggregator),
mCompatService(getCompatService()) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -512,8 +545,8 @@
drainInboundQueueLocked();
}
- while (!mConnectionsByFd.empty()) {
- sp<Connection> connection = mConnectionsByFd.begin()->second;
+ while (!mConnectionsByToken.empty()) {
+ sp<Connection> connection = mConnectionsByToken.begin()->second;
removeInputChannel(connection->inputChannel->getConnectionToken());
}
}
@@ -2410,6 +2443,8 @@
inputTarget.inputChannel = inputChannel;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+ inputTarget.displaySize =
+ int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
}
@@ -2878,6 +2913,8 @@
"event",
connection->getInputChannelName().c_str());
#endif
+ // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because
+ // this is a one-to-one event conversion.
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
@@ -2910,6 +2947,12 @@
ATRACE_NAME(message.c_str());
}
+ if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
+ (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {
+ // Skip reporting pointer down outside focus to the policy.
+ break;
+ }
+
dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
inputTarget.inputChannel->getConnectionToken());
@@ -2939,7 +2982,7 @@
// Enqueue the dispatch entry.
connection->outboundQueue.push_back(dispatchEntry.release());
- traceOutboundQueueLength(connection);
+ traceOutboundQueueLength(*connection);
}
/**
@@ -3115,10 +3158,11 @@
motionEntry.xPrecision, motionEntry.yPrecision,
motionEntry.xCursorPosition,
motionEntry.yCursorPosition,
+ dispatchEntry->displaySize.x,
+ dispatchEntry->displaySize.y,
motionEntry.downTime, motionEntry.eventTime,
motionEntry.pointerCount,
motionEntry.pointerProperties, usingCoords);
- reportTouchEventForStatistics(motionEntry);
break;
}
@@ -3192,13 +3236,13 @@
connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
connection->outboundQueue.end(),
dispatchEntry));
- traceOutboundQueueLength(connection);
+ traceOutboundQueueLength(*connection);
connection->waitQueue.push_back(dispatchEntry);
if (connection->responsive) {
mAnrTracker.insert(dispatchEntry->timeoutTime,
connection->inputChannel->getConnectionToken());
}
- traceWaitQueueLength(connection);
+ traceWaitQueueLength(*connection);
}
}
@@ -3267,9 +3311,9 @@
// Clear the dispatch queues.
drainDispatchQueue(connection->outboundQueue);
- traceOutboundQueueLength(connection);
+ traceOutboundQueueLength(*connection);
drainDispatchQueue(connection->waitQueue);
- traceWaitQueueLength(connection);
+ traceWaitQueueLength(*connection);
// The connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
@@ -3298,86 +3342,85 @@
delete dispatchEntry;
}
-int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
- InputDispatcher* d = static_cast<InputDispatcher*>(data);
+int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
+ std::scoped_lock _l(mLock);
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ ALOGW("Received looper callback for unknown input channel token %p. events=0x%x",
+ connectionToken.get(), events);
+ return 0; // remove the callback
+ }
- { // acquire lock
- std::scoped_lock _l(d->mLock);
-
- if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
- ALOGE("Received spurious receive callback for unknown input channel. "
- "fd=%d, events=0x%x",
- fd, events);
- return 0; // remove the callback
+ bool notify;
+ if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
+ if (!(events & ALOOPER_EVENT_INPUT)) {
+ ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x",
+ connection->getInputChannelName().c_str(), events);
+ return 1;
}
- bool notify;
- sp<Connection> connection = d->mConnectionsByFd[fd];
- if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
- if (!(events & ALOOPER_EVENT_INPUT)) {
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x",
- connection->getInputChannelName().c_str(), events);
+ nsecs_t currentTime = now();
+ bool gotOne = false;
+ status_t status = OK;
+ for (;;) {
+ Result<InputPublisher::ConsumerResponse> result =
+ connection->inputPublisher.receiveConsumerResponse();
+ if (!result.ok()) {
+ status = result.error().code();
+ break;
+ }
+
+ if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+ const InputPublisher::Finished& finish =
+ std::get<InputPublisher::Finished>(*result);
+ finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,
+ finish.consumeTime);
+ } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+ if (shouldReportMetricsForConnection(*connection)) {
+ const InputPublisher::Timeline& timeline =
+ std::get<InputPublisher::Timeline>(*result);
+ mLatencyTracker
+ .trackGraphicsLatency(timeline.inputEventId,
+ connection->inputChannel->getConnectionToken(),
+ std::move(timeline.graphicsTimeline));
+ }
+ }
+ gotOne = true;
+ }
+ if (gotOne) {
+ runCommandsLockedInterruptible();
+ if (status == WOULD_BLOCK) {
return 1;
}
-
- nsecs_t currentTime = now();
- bool gotOne = false;
- status_t status = OK;
- for (;;) {
- Result<InputPublisher::ConsumerResponse> result =
- connection->inputPublisher.receiveConsumerResponse();
- if (!result.ok()) {
- status = result.error().code();
- break;
- }
-
- if (std::holds_alternative<InputPublisher::Finished>(*result)) {
- const InputPublisher::Finished& finish =
- std::get<InputPublisher::Finished>(*result);
- d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
- finish.handled, finish.consumeTime);
- } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
- // TODO(b/167947340): Report this data to LatencyTracker
- }
- gotOne = true;
- }
- if (gotOne) {
- d->runCommandsLockedInterruptible();
- if (status == WOULD_BLOCK) {
- return 1;
- }
- }
-
- notify = status != DEAD_OBJECT || !connection->monitor;
- if (notify) {
- ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
- connection->getInputChannelName().c_str(), statusToString(status).c_str(),
- status);
- }
- } else {
- // Monitor channels are never explicitly unregistered.
- // We do it automatically when the remote endpoint is closed so don't warn about them.
- const bool stillHaveWindowHandle =
- d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
- nullptr;
- notify = !connection->monitor && stillHaveWindowHandle;
- if (notify) {
- ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. "
- "events=0x%x",
- connection->getInputChannelName().c_str(), events);
- }
}
- // Remove the channel.
- d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
- return 0; // remove the callback
- } // release lock
+ notify = status != DEAD_OBJECT || !connection->monitor;
+ if (notify) {
+ ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
+ connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+ status);
+ }
+ } else {
+ // Monitor channels are never explicitly unregistered.
+ // We do it automatically when the remote endpoint is closed so don't warn about them.
+ const bool stillHaveWindowHandle =
+ getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr;
+ notify = !connection->monitor && stillHaveWindowHandle;
+ if (notify) {
+ ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
+ connection->getInputChannelName().c_str(), events);
+ }
+ }
+
+ // Remove the channel.
+ removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
+ return 0; // remove the callback
}
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) {
- for (const auto& [fd, connection] : mConnectionsByFd) {
+ for (const auto& [token, connection] : mConnectionsByToken) {
synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
@@ -3827,7 +3870,8 @@
args->action, args->actionButton, args->flags, args->edgeFlags,
args->metaState, args->buttonState, args->classification, transform,
args->xPrecision, args->yPrecision, args->xCursorPosition,
- args->yCursorPosition, args->downTime, args->eventTime,
+ args->yCursorPosition, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
args->pointerCount, args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
@@ -3849,6 +3893,12 @@
args->xCursorPosition, args->yCursorPosition,
args->downTime, args->pointerCount,
args->pointerProperties, args->pointerCoords, 0, 0);
+ if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
+ IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
+ !mInputFilterEnabled) {
+ const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN;
+ mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime);
+ }
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
@@ -4342,11 +4392,11 @@
std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
const sp<IBinder>& token) const {
- size_t count = mInputChannelsByToken.count(token);
- if (count == 0) {
+ auto connectionIt = mConnectionsByToken.find(token);
+ if (connectionIt == mConnectionsByToken.end()) {
return nullptr;
}
- return mInputChannelsByToken.at(token);
+ return connectionIt->second->inputChannel;
}
void InputDispatcher::updateWindowHandlesForDisplayLocked(
@@ -4796,6 +4846,40 @@
return true;
}
+// Binder call
+bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) {
+ sp<IBinder> fromToken;
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+ if (toWindowHandle == nullptr) {
+ ALOGW("Could not find window associated with token=%p", destChannelToken.get());
+ return false;
+ }
+
+ const int32_t displayId = toWindowHandle->getInfo()->displayId;
+
+ auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+ if (touchStateIt == mTouchStatesByDisplay.end()) {
+ ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
+ displayId);
+ return false;
+ }
+
+ TouchState& state = touchStateIt->second;
+ if (state.windows.size() != 1) {
+ ALOGW("Cannot transfer touch state because there are %zu windows being touched",
+ state.windows.size());
+ return false;
+ }
+ const TouchedWindow& touchedWindow = state.windows[0];
+ fromToken = touchedWindow.windowHandle->getToken();
+ } // release lock
+
+ return transferTouchFocus(fromToken, destChannelToken);
+}
+
void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
if (DEBUG_FOCUS) {
ALOGD("Resetting and dropping all events (%s).", reason);
@@ -5022,13 +5106,13 @@
dump += INDENT "ReplacedKeys: <empty>\n";
}
- if (!mConnectionsByFd.empty()) {
+ if (!mConnectionsByToken.empty()) {
dump += INDENT "Connections:\n";
- for (const auto& pair : mConnectionsByFd) {
- const sp<Connection>& connection = pair.second;
+ for (const auto& [token, connection] : mConnectionsByToken) {
dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
"status=%s, monitor=%s, responsive=%s\n",
- pair.first, connection->getInputChannelName().c_str(),
+ connection->inputChannel->getFd().get(),
+ connection->getInputChannelName().c_str(),
connection->getWindowName().c_str(), connection->getStatusLabel(),
toString(connection->monitor), toString(connection->responsive));
@@ -5064,6 +5148,8 @@
dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
ns2ms(mConfig.keyRepeatTimeout));
+ dump += mLatencyTracker.dump(INDENT2);
+ dump += mLatencyAggregator.dump(INDENT2);
}
void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
@@ -5076,14 +5162,23 @@
}
}
+class LooperEventCallback : public LooperCallback {
+public:
+ LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
+ int handleEvent(int /*fd*/, int events, void* /*data*/) override { return mCallback(events); }
+
+private:
+ std::function<int(int events)> mCallback;
+};
+
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
#if DEBUG_CHANNEL_CREATION
ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
- std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
- status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+ status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
return base::Error(result) << "Failed to open input channel pair with name " << name;
@@ -5091,13 +5186,20 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
-
+ const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
- mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+ sp<Connection> connection =
+ new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+ if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+ mConnectionsByToken.emplace(token, connection);
+
+ std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+ this, std::placeholders::_1, token);
+
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
} // release lock
// Wake the looper because some connections have changed.
@@ -5125,16 +5227,23 @@
}
sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
-
+ const sp<IBinder>& token = serverChannel->getConnectionToken();
const int fd = serverChannel->getFd();
- mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+
+ if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+ mConnectionsByToken.emplace(token, connection);
+ std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+ this, std::placeholders::_1, token);
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
monitorsByDisplay[displayId].emplace_back(serverChannel, pid);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
+ ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(),
+ displayId, toString(isGestureMonitor), pid);
}
// Wake the looper because some connections have changed.
@@ -5167,7 +5276,6 @@
}
removeConnectionLocked(connection);
- mInputChannelsByToken.erase(connectionToken);
if (connection->monitor) {
removeMonitorChannelLocked(connectionToken);
@@ -5195,6 +5303,8 @@
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
if (monitors[i].inputChannel->getConnectionToken() == connectionToken) {
+ ALOGI("Erasing monitor %s on display %" PRId32 ", pid=%" PRId32,
+ monitors[i].inputChannel->getName().c_str(), it->first, monitors[i].pid);
monitors.erase(monitors.begin() + i);
break;
}
@@ -5323,9 +5433,8 @@
return nullptr;
}
- for (const auto& pair : mConnectionsByFd) {
- const sp<Connection>& connection = pair.second;
- if (connection->inputChannel->getConnectionToken() == inputConnectionToken) {
+ for (const auto& [token, connection] : mConnectionsByToken) {
+ if (token == inputConnectionToken) {
return connection;
}
}
@@ -5343,7 +5452,7 @@
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- removeByValue(mConnectionsByFd, connection);
+ mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
}
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
@@ -5616,7 +5725,12 @@
ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
}
- reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);
+ if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
+ mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
+ connection->inputChannel->getConnectionToken(),
+ dispatchEntry->deliveryTime, commandEntry->consumeTime,
+ finishTime);
+ }
bool restartEvent;
if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
@@ -5648,10 +5762,10 @@
processConnectionResponsiveLocked(*connection);
}
}
- traceWaitQueueLength(connection);
+ traceWaitQueueLength(*connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.push_front(dispatchEntry);
- traceOutboundQueueLength(connection);
+ traceOutboundQueueLength(*connection);
} else {
releaseDispatchEntry(dispatchEntry);
}
@@ -5928,58 +6042,25 @@
mLock.lock();
}
-void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
- const Connection& connection, bool handled) {
- // TODO Write some statistics about how long we spend waiting.
-}
-
-/**
- * Report the touch event latency to the statsd server.
- * Input events are reported for statistics if:
- * - This is a touchscreen event
- * - InputFilter is not enabled
- * - Event is not injected or synthesized
- *
- * Statistics should be reported before calling addValue, to prevent a fresh new sample
- * from getting aggregated with the "old" data.
- */
-void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry)
- REQUIRES(mLock) {
- const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) &&
- !(motionEntry.isSynthesized()) && !mInputFilterEnabled;
- if (!reportForStatistics) {
- return;
- }
-
- if (mTouchStatistics.shouldReport()) {
- android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(),
- mTouchStatistics.getMax(), mTouchStatistics.getMean(),
- mTouchStatistics.getStDev(), mTouchStatistics.getCount());
- mTouchStatistics.reset();
- }
- const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime);
- mTouchStatistics.addValue(latencyMicros);
-}
-
void InputDispatcher::traceInboundQueueLengthLocked() {
if (ATRACE_ENABLED()) {
ATRACE_INT("iq", mInboundQueue.size());
}
}
-void InputDispatcher::traceOutboundQueueLength(const sp<Connection>& connection) {
+void InputDispatcher::traceOutboundQueueLength(const Connection& connection) {
if (ATRACE_ENABLED()) {
char counterName[40];
- snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str());
- ATRACE_INT(counterName, connection->outboundQueue.size());
+ snprintf(counterName, sizeof(counterName), "oq:%s", connection.getWindowName().c_str());
+ ATRACE_INT(counterName, connection.outboundQueue.size());
}
}
-void InputDispatcher::traceWaitQueueLength(const sp<Connection>& connection) {
+void InputDispatcher::traceWaitQueueLength(const Connection& connection) {
if (ATRACE_ENABLED()) {
char counterName[40];
- snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str());
- ATRACE_INT(counterName, connection->waitQueue.size());
+ snprintf(counterName, sizeof(counterName), "wq:%s", connection.getWindowName().c_str());
+ ATRACE_INT(counterName, connection.waitQueue.size());
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 7ab4fd7..bb3f3e6 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -29,6 +29,8 @@
#include "InputState.h"
#include "InputTarget.h"
#include "InputThread.h"
+#include "LatencyAggregator.h"
+#include "LatencyTracker.h"
#include "Monitor.h"
#include "TouchState.h"
#include "TouchedWindow.h"
@@ -39,7 +41,6 @@
#include <input/InputApplication.h>
#include <input/InputTransport.h>
#include <input/InputWindow.h>
-#include <input/LatencyStatistics.h>
#include <limits.h>
#include <stddef.h>
#include <ui/Region.h>
@@ -81,60 +82,60 @@
*/
class InputDispatcher : public android::InputDispatcherInterface {
protected:
- virtual ~InputDispatcher();
+ ~InputDispatcher() override;
public:
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
- virtual bool waitForIdle() override;
- virtual status_t start() override;
- virtual status_t stop() override;
+ void dump(std::string& dump) override;
+ void monitor() override;
+ bool waitForIdle() override;
+ status_t start() override;
+ status_t stop() override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
- virtual void notifyKey(const NotifyKeyArgs* args) override;
- virtual void notifyMotion(const NotifyMotionArgs* args) override;
- virtual void notifySwitch(const NotifySwitchArgs* args) override;
- virtual void notifySensor(const NotifySensorArgs* args) override;
- virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+ void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ void notifyKey(const NotifyKeyArgs* args) override;
+ void notifyMotion(const NotifyMotionArgs* args) override;
+ void notifySwitch(const NotifySwitchArgs* args) override;
+ void notifySensor(const NotifySensorArgs* args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+ void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
- virtual android::os::InputEventInjectionResult injectInputEvent(
+ android::os::InputEventInjectionResult injectInputEvent(
const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) override;
- virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+ std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
- virtual void setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
- handlesPerDisplay) override;
- virtual void setFocusedApplication(
+ void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+ handlesPerDisplay) override;
+ void setFocusedApplication(
int32_t displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
- virtual void setFocusedDisplay(int32_t displayId) override;
- virtual void setInputDispatchMode(bool enabled, bool frozen) override;
- virtual void setInputFilterEnabled(bool enabled) override;
- virtual void setInTouchMode(bool inTouchMode) override;
- virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
- virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
+ void setFocusedDisplay(int32_t displayId) override;
+ void setInputDispatchMode(bool enabled, bool frozen) override;
+ void setInputFilterEnabled(bool enabled) override;
+ void setInTouchMode(bool inTouchMode) override;
+ void setMaximumObscuringOpacityForTouch(float opacity) override;
+ void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop = false) override;
+ bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop = false) override;
+ bool transferTouch(const sp<IBinder>& destChannelToken) override;
- virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
- virtual void setFocusedWindow(const FocusRequest&) override;
- virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
- bool isGestureMonitor,
- const std::string& name,
- int32_t pid) override;
- virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
- virtual status_t pilferPointers(const sp<IBinder>& token) override;
- virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
- virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+ void setFocusedWindow(const FocusRequest&) override;
+ base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name,
+ int32_t pid) override;
+ status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
+ status_t pilferPointers(const sp<IBinder>& token) override;
+ void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
+ bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
@@ -211,9 +212,6 @@
bool addPortalWindows = false,
bool ignoreDragWindow = false) REQUIRES(mLock);
- // All registered connections mapped by channel file descriptor.
- std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
-
sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
@@ -225,8 +223,10 @@
struct StrongPointerHash {
std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
};
- std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, StrongPointerHash<IBinder>>
- mInputChannelsByToken GUARDED_BY(mLock);
+
+ // All registered connections mapped by input channel token.
+ std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
+ GUARDED_BY(mLock);
// Finds the display ID of the gesture monitor identified by the provided token.
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
@@ -544,7 +544,7 @@
bool notify) REQUIRES(mLock);
void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
- static int handleReceiveCallback(int fd, int events, void* data);
+ int handleReceiveCallback(int events, sp<IBinder> connectionToken);
// The action sent should only be of type AMOTION_EVENT_*
void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
const sp<IBinder>& newToken) REQUIRES(mLock);
@@ -637,15 +637,11 @@
void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
// Statistics gathering.
- static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min;
- LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
-
- void reportTouchEventForStatistics(const MotionEntry& entry);
- void reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
- const Connection& connection, bool handled);
+ LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
+ LatencyTracker mLatencyTracker GUARDED_BY(mLock);
void traceInboundQueueLengthLocked() REQUIRES(mLock);
- void traceOutboundQueueLength(const sp<Connection>& connection);
- void traceWaitQueueLength(const sp<Connection>& connection);
+ void traceOutboundQueueLength(const Connection& connection);
+ void traceWaitQueueLength(const Connection& connection);
sp<InputReporterInterface> mReporter;
sp<com::android::internal::compat::IPlatformCompatNative> mCompatService;
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp
new file mode 100644
index 0000000..3edb638
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 "InputEventTimeline.h"
+
+namespace android::inputdispatcher {
+
+ConnectionTimeline::ConnectionTimeline(nsecs_t deliveryTime, nsecs_t consumeTime,
+ nsecs_t finishTime)
+ : deliveryTime(deliveryTime),
+ consumeTime(consumeTime),
+ finishTime(finishTime),
+ mHasDispatchTimeline(true) {}
+
+ConnectionTimeline::ConnectionTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline)
+ : graphicsTimeline(std::move(graphicsTimeline)), mHasGraphicsTimeline(true) {}
+
+bool ConnectionTimeline::isComplete() const {
+ return mHasDispatchTimeline && mHasGraphicsTimeline;
+}
+
+bool ConnectionTimeline::setDispatchTimeline(nsecs_t inDeliveryTime, nsecs_t inConsumeTime,
+ nsecs_t inFinishTime) {
+ if (mHasDispatchTimeline) {
+ return false;
+ }
+ deliveryTime = inDeliveryTime;
+ consumeTime = inConsumeTime;
+ finishTime = inFinishTime;
+ mHasDispatchTimeline = true;
+ return true;
+}
+
+bool ConnectionTimeline::setGraphicsTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ if (mHasGraphicsTimeline) {
+ return false;
+ }
+ graphicsTimeline = std::move(timeline);
+ mHasGraphicsTimeline = true;
+ return true;
+}
+
+bool ConnectionTimeline::operator==(const ConnectionTimeline& rhs) const {
+ return deliveryTime == rhs.deliveryTime && consumeTime == rhs.consumeTime &&
+ finishTime == rhs.finishTime && graphicsTimeline == rhs.graphicsTimeline &&
+ mHasDispatchTimeline == rhs.mHasDispatchTimeline &&
+ mHasGraphicsTimeline == rhs.mHasGraphicsTimeline;
+}
+
+bool ConnectionTimeline::operator!=(const ConnectionTimeline& rhs) const {
+ return !operator==(rhs);
+}
+
+InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime)
+ : isDown(isDown), eventTime(eventTime), readTime(readTime) {}
+
+bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const {
+ if (connectionTimelines.size() != rhs.connectionTimelines.size()) {
+ return false;
+ }
+ for (const auto& [connectionToken, connectionTimeline] : connectionTimelines) {
+ auto it = rhs.connectionTimelines.find(connectionToken);
+ if (it == rhs.connectionTimelines.end()) {
+ return false;
+ }
+ if (connectionTimeline != it->second) {
+ return false;
+ }
+ }
+ return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime;
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
new file mode 100644
index 0000000..77b8472
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -0,0 +1,108 @@
+/*
+ * 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 _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
+
+#include <binder/IBinder.h>
+#include <input/Input.h>
+#include <unordered_map>
+
+namespace android {
+
+namespace inputdispatcher {
+
+/**
+ * Describes the input event timeline for each connection.
+ * An event with the same inputEventId can go to more than 1 connection simultaneously.
+ * For each connection that the input event goes to, there will be a separate ConnectionTimeline
+ * created.
+ * To create a complete ConnectionTimeline, we must receive two calls:
+ * 1) setDispatchTimeline
+ * 2) setGraphicsTimeline
+ *
+ * In a typical scenario, the dispatch timeline is known first. Later, if a frame is produced, the
+ * graphics timeline is available.
+ */
+struct ConnectionTimeline {
+ // DispatchTimeline
+ nsecs_t deliveryTime; // time at which the event was sent to the receiver
+ nsecs_t consumeTime; // time at which the receiver read the event
+ nsecs_t finishTime; // time at which the finish event was received
+ // GraphicsTimeline
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+
+ ConnectionTimeline(nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
+ ConnectionTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline);
+
+ /**
+ * True if all contained timestamps are valid, false otherwise.
+ */
+ bool isComplete() const;
+ /**
+ * Set the dispatching-related times. Return true if the operation succeeded, false if the
+ * dispatching times have already been set. If this function returns false, it likely indicates
+ * an error from the app side.
+ */
+ bool setDispatchTimeline(nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
+ /**
+ * Set the graphics-related times. Return true if the operation succeeded, false if the
+ * graphics times have already been set. If this function returns false, it likely indicates
+ * an error from the app side.
+ */
+ bool setGraphicsTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline);
+
+ inline bool operator==(const ConnectionTimeline& rhs) const;
+ inline bool operator!=(const ConnectionTimeline& rhs) const;
+
+private:
+ bool mHasDispatchTimeline = false;
+ bool mHasGraphicsTimeline = false;
+};
+
+struct InputEventTimeline {
+ InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime);
+ const bool isDown; // True if this is an ACTION_DOWN event
+ const nsecs_t eventTime;
+ const nsecs_t readTime;
+
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& b) const {
+ return std::hash<IBinder*>{}(b.get());
+ }
+ };
+
+ std::unordered_map<sp<IBinder>, ConnectionTimeline, IBinderHash> connectionTimelines;
+
+ bool operator==(const InputEventTimeline& rhs) const;
+};
+
+class InputEventTimelineProcessor {
+protected:
+ InputEventTimelineProcessor() {}
+ virtual ~InputEventTimelineProcessor() {}
+
+public:
+ /**
+ * Process the provided timeline
+ */
+ virtual void processTimeline(const InputEventTimeline& timeline) = 0;
+};
+
+} // namespace inputdispatcher
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index debf805..1c4980b 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -100,6 +100,9 @@
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
+ // Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
+ int2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp
new file mode 100644
index 0000000..a5bfc25
--- /dev/null
+++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LatencyAggregator"
+#include "LatencyAggregator.h"
+
+#include <inttypes.h>
+
+#include <android-base/stringprintf.h>
+#include <input/Input.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+
+using android::base::StringPrintf;
+using dist_proc::aggregation::KllQuantile;
+using std::chrono_literals::operator""ms;
+
+// Convert the provided nanoseconds into hundreds of microseconds.
+// Use hundreds of microseconds (as opposed to microseconds) to preserve space.
+static inline int64_t ns2hus(nsecs_t nanos) {
+ return ns2us(nanos) / 100;
+}
+
+// The maximum number of events that we will store in the statistics. Any events that we will
+// receive after we have reached this number will be ignored. We could also implement this by
+// checking the actual size of the current data and making sure that we do not go over. However,
+// the serialization process of sketches is too heavy (1 ms for all 14 sketches), and would be too
+// much to do (even if infrequently).
+// The value here has been determined empirically.
+static constexpr size_t MAX_EVENTS_FOR_STATISTICS = 20000;
+
+// Category (=namespace) name for the input settings that are applied at boot time
+static const char* INPUT_NATIVE_BOOT = "input_native_boot";
+// Feature flag name for the threshold of end-to-end touch latency that would trigger
+// SlowEventReported atom to be pushed
+static const char* SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS =
+ "slow_event_min_reporting_latency_millis";
+// Feature flag name for the minimum delay before reporting a slow event after having just reported
+// a slow event. This helps limit the amount of data sent to the server
+static const char* SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS =
+ "slow_event_min_reporting_interval_millis";
+
+// If an event has end-to-end latency > 200 ms, it will get reported as a slow event.
+std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY = 200ms;
+// If we receive two slow events less than 1 min apart, we will only report 1 of them.
+std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL = 60000ms;
+
+static std::chrono::milliseconds getSlowEventMinReportingLatency() {
+ std::string millis = server_configurable_flags::
+ GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS,
+ std::to_string(
+ DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY.count()));
+ return std::chrono::milliseconds(std::stoi(millis));
+}
+
+static std::chrono::milliseconds getSlowEventMinReportingInterval() {
+ std::string millis = server_configurable_flags::
+ GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS,
+ std::to_string(
+ DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL.count()));
+ return std::chrono::milliseconds(std::stoi(millis));
+}
+
+namespace android::inputdispatcher {
+
+/**
+ * Same as android::util::BytesField, but doesn't store raw pointers, and therefore deletes its
+ * resources automatically.
+ */
+class SafeBytesField {
+public:
+ explicit SafeBytesField(dist_proc::aggregation::KllQuantile& quantile) {
+ const zetasketch::android::AggregatorStateProto aggProto = quantile.SerializeToProto();
+ mBuffer.resize(aggProto.ByteSizeLong());
+ aggProto.SerializeToArray(mBuffer.data(), mBuffer.size());
+ }
+ android::util::BytesField getBytesField() {
+ return android::util::BytesField(mBuffer.data(), mBuffer.size());
+ }
+
+private:
+ std::vector<char> mBuffer;
+};
+
+LatencyAggregator::LatencyAggregator() {
+ AStatsManager_setPullAtomCallback(android::util::INPUT_EVENT_LATENCY_SKETCH, nullptr,
+ LatencyAggregator::pullAtomCallback, this);
+ dist_proc::aggregation::KllQuantileOptions options;
+ options.set_inv_eps(100); // Request precision of 1.0%, instead of default 0.1%
+ for (size_t i = 0; i < SketchIndex::SIZE; i++) {
+ mDownSketches[i] = KllQuantile::Create(options);
+ mMoveSketches[i] = KllQuantile::Create(options);
+ }
+}
+
+LatencyAggregator::~LatencyAggregator() {
+ AStatsManager_clearPullAtomCallback(android::util::INPUT_EVENT_LATENCY_SKETCH);
+}
+
+AStatsManager_PullAtomCallbackReturn LatencyAggregator::pullAtomCallback(int32_t atomTag,
+ AStatsEventList* data,
+ void* cookie) {
+ LatencyAggregator* pAggregator = reinterpret_cast<LatencyAggregator*>(cookie);
+ if (pAggregator == nullptr) {
+ LOG_ALWAYS_FATAL("pAggregator is null!");
+ }
+ return pAggregator->pullData(data);
+}
+
+void LatencyAggregator::processTimeline(const InputEventTimeline& timeline) {
+ processStatistics(timeline);
+ processSlowEvent(timeline);
+}
+
+void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) {
+ // Before we do any processing, check that we have not yet exceeded MAX_SIZE
+ if (mNumSketchEventsProcessed >= MAX_EVENTS_FOR_STATISTICS) {
+ return;
+ }
+ mNumSketchEventsProcessed++;
+
+ std::array<std::unique_ptr<KllQuantile>, SketchIndex::SIZE>& sketches =
+ timeline.isDown ? mDownSketches : mMoveSketches;
+
+ // Process common ones first
+ const nsecs_t eventToRead = timeline.readTime - timeline.eventTime;
+ sketches[SketchIndex::EVENT_TO_READ]->Add(ns2hus(eventToRead));
+
+ // Now process per-connection ones
+ for (const auto& [connectionToken, connectionTimeline] : timeline.connectionTimelines) {
+ if (!connectionTimeline.isComplete()) {
+ continue;
+ }
+ const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime;
+ const nsecs_t deliverToConsume =
+ connectionTimeline.consumeTime - connectionTimeline.deliveryTime;
+ const nsecs_t consumeToFinish =
+ connectionTimeline.finishTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompletedTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime;
+ const nsecs_t endToEnd = presentTime - timeline.eventTime;
+
+ sketches[SketchIndex::READ_TO_DELIVER]->Add(ns2hus(readToDeliver));
+ sketches[SketchIndex::DELIVER_TO_CONSUME]->Add(ns2hus(deliverToConsume));
+ sketches[SketchIndex::CONSUME_TO_FINISH]->Add(ns2hus(consumeToFinish));
+ sketches[SketchIndex::CONSUME_TO_GPU_COMPLETE]->Add(ns2hus(consumeToGpuComplete));
+ sketches[SketchIndex::GPU_COMPLETE_TO_PRESENT]->Add(ns2hus(gpuCompleteToPresent));
+ sketches[SketchIndex::END_TO_END]->Add(ns2hus(endToEnd));
+ }
+}
+
+AStatsManager_PullAtomCallbackReturn LatencyAggregator::pullData(AStatsEventList* data) {
+ std::array<std::unique_ptr<SafeBytesField>, SketchIndex::SIZE> serializedDownData;
+ std::array<std::unique_ptr<SafeBytesField>, SketchIndex::SIZE> serializedMoveData;
+ for (size_t i = 0; i < SketchIndex::SIZE; i++) {
+ serializedDownData[i] = std::make_unique<SafeBytesField>(*mDownSketches[i]);
+ serializedMoveData[i] = std::make_unique<SafeBytesField>(*mMoveSketches[i]);
+ }
+ android::util::
+ addAStatsEvent(data, android::util::INPUT_EVENT_LATENCY_SKETCH,
+ // DOWN sketches
+ serializedDownData[SketchIndex::EVENT_TO_READ]->getBytesField(),
+ serializedDownData[SketchIndex::READ_TO_DELIVER]->getBytesField(),
+ serializedDownData[SketchIndex::DELIVER_TO_CONSUME]->getBytesField(),
+ serializedDownData[SketchIndex::CONSUME_TO_FINISH]->getBytesField(),
+ serializedDownData[SketchIndex::CONSUME_TO_GPU_COMPLETE]
+ ->getBytesField(),
+ serializedDownData[SketchIndex::GPU_COMPLETE_TO_PRESENT]
+ ->getBytesField(),
+ serializedDownData[SketchIndex::END_TO_END]->getBytesField(),
+ // MOVE sketches
+ serializedMoveData[SketchIndex::EVENT_TO_READ]->getBytesField(),
+ serializedMoveData[SketchIndex::READ_TO_DELIVER]->getBytesField(),
+ serializedMoveData[SketchIndex::DELIVER_TO_CONSUME]->getBytesField(),
+ serializedMoveData[SketchIndex::CONSUME_TO_FINISH]->getBytesField(),
+ serializedMoveData[SketchIndex::CONSUME_TO_GPU_COMPLETE]
+ ->getBytesField(),
+ serializedMoveData[SketchIndex::GPU_COMPLETE_TO_PRESENT]
+ ->getBytesField(),
+ serializedMoveData[SketchIndex::END_TO_END]->getBytesField());
+
+ for (size_t i = 0; i < SketchIndex::SIZE; i++) {
+ mDownSketches[i]->Reset();
+ mMoveSketches[i]->Reset();
+ }
+ // Start new aggregations
+ mNumSketchEventsProcessed = 0;
+ return AStatsManager_PULL_SUCCESS;
+}
+
+void LatencyAggregator::processSlowEvent(const InputEventTimeline& timeline) {
+ static const std::chrono::duration sSlowEventThreshold = getSlowEventMinReportingLatency();
+ static const std::chrono::duration sSlowEventReportingInterval =
+ getSlowEventMinReportingInterval();
+ for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) {
+ if (!connectionTimeline.isComplete()) {
+ continue;
+ }
+ mNumEventsSinceLastSlowEventReport++;
+ const nsecs_t presentTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ const std::chrono::nanoseconds endToEndLatency =
+ std::chrono::nanoseconds(presentTime - timeline.eventTime);
+ if (endToEndLatency < sSlowEventThreshold) {
+ continue;
+ }
+ // This is a slow event. Before we report it, check if we are reporting too often
+ const std::chrono::duration elapsedSinceLastReport =
+ std::chrono::nanoseconds(timeline.eventTime - mLastSlowEventTime);
+ if (elapsedSinceLastReport < sSlowEventReportingInterval) {
+ mNumSkippedSlowEvents++;
+ continue;
+ }
+
+ const nsecs_t eventToRead = timeline.readTime - timeline.eventTime;
+ const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime;
+ const nsecs_t deliverToConsume =
+ connectionTimeline.consumeTime - connectionTimeline.deliveryTime;
+ const nsecs_t consumeToFinish =
+ connectionTimeline.finishTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompletedTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime;
+
+ android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, timeline.isDown,
+ static_cast<int32_t>(ns2us(eventToRead)),
+ static_cast<int32_t>(ns2us(readToDeliver)),
+ static_cast<int32_t>(ns2us(deliverToConsume)),
+ static_cast<int32_t>(ns2us(consumeToFinish)),
+ static_cast<int32_t>(ns2us(consumeToGpuComplete)),
+ static_cast<int32_t>(ns2us(gpuCompleteToPresent)),
+ static_cast<int32_t>(ns2us(endToEndLatency.count())),
+ static_cast<int32_t>(mNumEventsSinceLastSlowEventReport),
+ static_cast<int32_t>(mNumSkippedSlowEvents));
+ mNumEventsSinceLastSlowEventReport = 0;
+ mNumSkippedSlowEvents = 0;
+ mLastSlowEventTime = timeline.readTime;
+ }
+}
+
+std::string LatencyAggregator::dump(const char* prefix) {
+ std::string sketchDump = StringPrintf("%s Sketches:\n", prefix);
+ for (size_t i = 0; i < SketchIndex::SIZE; i++) {
+ const int64_t numDown = mDownSketches[i]->num_values();
+ SafeBytesField downBytesField(*mDownSketches[i]);
+ const float downBytesKb = downBytesField.getBytesField().arg_length * 1E-3;
+ const int64_t numMove = mMoveSketches[i]->num_values();
+ SafeBytesField moveBytesField(*mMoveSketches[i]);
+ const float moveBytesKb = moveBytesField.getBytesField().arg_length * 1E-3;
+ sketchDump +=
+ StringPrintf("%s mDownSketches[%zu]->num_values = %" PRId64 " size = %.1fKB"
+ " mMoveSketches[%zu]->num_values = %" PRId64 " size = %.1fKB\n",
+ prefix, i, numDown, downBytesKb, i, numMove, moveBytesKb);
+ }
+
+ return StringPrintf("%sLatencyAggregator:\n", prefix) + sketchDump +
+ StringPrintf("%s mNumSketchEventsProcessed=%zu\n", prefix, mNumSketchEventsProcessed) +
+ StringPrintf("%s mLastSlowEventTime=%" PRId64 "\n", prefix, mLastSlowEventTime) +
+ StringPrintf("%s mNumEventsSinceLastSlowEventReport = %zu\n", prefix,
+ mNumEventsSinceLastSlowEventReport) +
+ StringPrintf("%s mNumSkippedSlowEvents = %zu\n", prefix, mNumSkippedSlowEvents);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h
new file mode 100644
index 0000000..ed5731f
--- /dev/null
+++ b/services/inputflinger/dispatcher/LatencyAggregator.h
@@ -0,0 +1,90 @@
+/*
+ * 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 _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
+#define _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
+
+#include <kll.h>
+#include <statslog.h>
+#include <utils/Timers.h>
+
+#include "InputEventTimeline.h"
+
+namespace android::inputdispatcher {
+
+enum SketchIndex : size_t {
+ EVENT_TO_READ = 0,
+ READ_TO_DELIVER = 1,
+ DELIVER_TO_CONSUME = 2,
+ CONSUME_TO_FINISH = 3,
+ CONSUME_TO_GPU_COMPLETE = 4,
+ GPU_COMPLETE_TO_PRESENT = 5,
+ END_TO_END = 6, // EVENT_TO_PRESENT
+ SIZE = 7, // Must be last
+};
+
+// Let's create a full timeline here:
+// eventTime
+// readTime
+// <---- after this point, the data becomes per-connection
+// deliveryTime // time at which the event was sent to the receiver
+// consumeTime // time at which the receiver read the event
+// finishTime // time at which the finish event was received
+// GraphicsTimeline::GPU_COMPLETED_TIME
+// GraphicsTimeline::PRESENT_TIME
+
+/**
+ * Keep sketches of the provided events and report slow events
+ */
+class LatencyAggregator final : public InputEventTimelineProcessor {
+public:
+ LatencyAggregator();
+ /**
+ * Record a complete event timeline
+ */
+ void processTimeline(const InputEventTimeline& timeline) override;
+
+ std::string dump(const char* prefix);
+
+ ~LatencyAggregator();
+
+private:
+ static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie);
+ AStatsManager_PullAtomCallbackReturn pullData(AStatsEventList* data);
+ // ---------- Slow event handling ----------
+ void processSlowEvent(const InputEventTimeline& timeline);
+ nsecs_t mLastSlowEventTime = 0;
+ // How many slow events have been skipped due to rate limiting
+ size_t mNumSkippedSlowEvents = 0;
+ // How many events have been received since the last time we reported a slow event
+ size_t mNumEventsSinceLastSlowEventReport = 0;
+
+ // ---------- Statistics handling ----------
+ void processStatistics(const InputEventTimeline& timeline);
+ // Sketches
+ std::array<std::unique_ptr<dist_proc::aggregation::KllQuantile>, SketchIndex::SIZE>
+ mDownSketches;
+ std::array<std::unique_ptr<dist_proc::aggregation::KllQuantile>, SketchIndex::SIZE>
+ mMoveSketches;
+ // How many events have been processed so far
+ size_t mNumSketchEventsProcessed = 0;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
new file mode 100644
index 0000000..d634dcd
--- /dev/null
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LatencyTracker"
+#include "LatencyTracker.h"
+
+#include <inttypes.h>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android/os/IInputConstants.h>
+#include <input/Input.h>
+#include <log/log.h>
+
+using android::base::HwTimeoutMultiplier;
+using android::base::StringPrintf;
+
+namespace android::inputdispatcher {
+
+/**
+ * Events that are older than this time will be considered mature, at which point we will stop
+ * waiting for the apps to provide further information about them.
+ * It's likely that the apps will ANR if the events are not received by this deadline, and we
+ * already track ANR metrics separately.
+ */
+const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
+ android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ HwTimeoutMultiplier());
+
+static bool isMatureEvent(nsecs_t eventTime, nsecs_t now) {
+ std::chrono::duration age = std::chrono::nanoseconds(now) - std::chrono::nanoseconds(eventTime);
+ return age > ANR_TIMEOUT;
+}
+
+/**
+ * A multimap allows to have several entries with the same key. This function just erases a specific
+ * key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value).
+ */
+template <typename K, typename V>
+static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) {
+ auto iterpair = map.equal_range(key);
+
+ for (auto it = iterpair.first; it != iterpair.second; ++it) {
+ if (it->second == value) {
+ map.erase(it);
+ break;
+ }
+ }
+}
+
+LatencyTracker::LatencyTracker(InputEventTimelineProcessor* processor)
+ : mTimelineProcessor(processor) {
+ LOG_ALWAYS_FATAL_IF(processor == nullptr);
+}
+
+void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime,
+ nsecs_t readTime) {
+ reportAndPruneMatureRecords(eventTime);
+ const auto it = mTimelines.find(inputEventId);
+ if (it != mTimelines.end()) {
+ // Input event ids are randomly generated, so it's possible that two events have the same
+ // event id. Drop this event, and also drop the existing event because the apps would
+ // confuse us by reporting the rest of the timeline for one of them. This should happen
+ // rarely, so we won't lose much data
+ mTimelines.erase(it);
+ // In case we have another input event with a different id and at the same eventTime,
+ // only erase this specific inputEventId.
+ eraseByKeyAndValue(mEventTimes, eventTime, inputEventId);
+ return;
+ }
+ mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime));
+ mEventTimes.emplace(eventTime, inputEventId);
+}
+
+void LatencyTracker::trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
+ nsecs_t deliveryTime, nsecs_t consumeTime,
+ nsecs_t finishTime) {
+ const auto it = mTimelines.find(inputEventId);
+ if (it == mTimelines.end()) {
+ // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do
+ // anything in its process. Just drop the report and move on.
+ return;
+ }
+
+ InputEventTimeline& timeline = it->second;
+ const auto connectionIt = timeline.connectionTimelines.find(connectionToken);
+ if (connectionIt == timeline.connectionTimelines.end()) {
+ // Most likely case: app calls 'finishInputEvent' before it reports the graphics timeline
+ timeline.connectionTimelines.emplace(connectionToken,
+ ConnectionTimeline{deliveryTime, consumeTime,
+ finishTime});
+ } else {
+ // Already have a record for this connectionToken
+ ConnectionTimeline& connectionTimeline = connectionIt->second;
+ const bool success =
+ connectionTimeline.setDispatchTimeline(deliveryTime, consumeTime, finishTime);
+ if (!success) {
+ // We are receiving unreliable data from the app. Just delete the entire connection
+ // timeline for this event
+ timeline.connectionTimelines.erase(connectionIt);
+ }
+ }
+}
+
+void LatencyTracker::trackGraphicsLatency(
+ int32_t inputEventId, const sp<IBinder>& connectionToken,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
+ const auto it = mTimelines.find(inputEventId);
+ if (it == mTimelines.end()) {
+ // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do
+ // anything in its process. Just drop the report and move on.
+ return;
+ }
+
+ InputEventTimeline& timeline = it->second;
+ const auto connectionIt = timeline.connectionTimelines.find(connectionToken);
+ if (connectionIt == timeline.connectionTimelines.end()) {
+ timeline.connectionTimelines.emplace(connectionToken, std::move(graphicsTimeline));
+ } else {
+ // Most likely case
+ ConnectionTimeline& connectionTimeline = connectionIt->second;
+ const bool success = connectionTimeline.setGraphicsTimeline(std::move(graphicsTimeline));
+ if (!success) {
+ // We are receiving unreliable data from the app. Just delete the entire connection
+ // timeline for this event
+ timeline.connectionTimelines.erase(connectionIt);
+ }
+ }
+}
+
+/**
+ * We should use the current time 'now()' here to determine the age of the event, but instead we
+ * are using the latest 'eventTime' for efficiency since this time is already acquired, and
+ * 'trackListener' should happen soon after the event occurs.
+ */
+void LatencyTracker::reportAndPruneMatureRecords(nsecs_t newEventTime) {
+ while (!mEventTimes.empty()) {
+ const auto& [oldestEventTime, oldestInputEventId] = *mEventTimes.begin();
+ if (isMatureEvent(oldestEventTime, newEventTime /*now*/)) {
+ // Report and drop this event
+ const auto it = mTimelines.find(oldestInputEventId);
+ LOG_ALWAYS_FATAL_IF(it == mTimelines.end(),
+ "Event %" PRId32 " is in mEventTimes, but not in mTimelines",
+ oldestInputEventId);
+ const InputEventTimeline& timeline = it->second;
+ mTimelineProcessor->processTimeline(timeline);
+ mTimelines.erase(it);
+ mEventTimes.erase(mEventTimes.begin());
+ } else {
+ // If the oldest event does not need to be pruned, no events should be pruned.
+ return;
+ }
+ }
+}
+
+void LatencyTracker::reportNow() {
+ for (const auto& [inputEventId, timeline] : mTimelines) {
+ mTimelineProcessor->processTimeline(timeline);
+ }
+ mTimelines.clear();
+ mEventTimes.clear();
+}
+
+std::string LatencyTracker::dump(const char* prefix) {
+ return StringPrintf("%sLatencyTracker:\n", prefix) +
+ StringPrintf("%s mTimelines.size() = %zu\n", prefix, mTimelines.size()) +
+ StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
new file mode 100644
index 0000000..289b8ed
--- /dev/null
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -0,0 +1,87 @@
+/*
+ * 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 _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
+#define _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
+
+#include <map>
+#include <unordered_map>
+
+#include <binder/IBinder.h>
+#include <input/Input.h>
+
+#include "InputEventTimeline.h"
+
+namespace android::inputdispatcher {
+
+/**
+ * Maintain a record for input events that are received by InputDispatcher, sent out to the apps,
+ * and processed by the apps. Once an event becomes "mature" (older than the ANR timeout), report
+ * the entire input event latency history to the reporting function.
+ *
+ * All calls to LatencyTracker should come from the same thread. It is not thread-safe.
+ */
+class LatencyTracker {
+public:
+ /**
+ * Create a LatencyTracker.
+ * param reportingFunction: the function that will be called in order to report full latency.
+ */
+ LatencyTracker(InputEventTimelineProcessor* processor);
+ /**
+ * Start keeping track of an event identified by inputEventId. This must be called first.
+ */
+ void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime);
+ void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
+ nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
+ void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
+ /**
+ * Report all collected events immediately, even if some of them are currently incomplete
+ * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future.
+ * This is useful for tests. Otherwise, tests would have to inject additional "future" events,
+ * which is not convenient.
+ */
+ void reportNow();
+
+ std::string dump(const char* prefix);
+
+private:
+ /**
+ * A collection of InputEventTimelines keyed by inputEventId. An InputEventTimeline is first
+ * created when 'trackListener' is called.
+ * When either 'trackFinishedEvent' or 'trackGraphicsLatency' is called for this input event,
+ * the corresponding InputEventTimeline will be updated for that token.
+ */
+ std::unordered_map<int32_t /*inputEventId*/, InputEventTimeline> mTimelines;
+ /**
+ * The collection of eventTimes will help us quickly find the events that we should prune
+ * from the 'mTimelines'. Since 'mTimelines' is keyed by inputEventId, it would be inefficient
+ * to walk through it directly to find the oldest input events to get rid of.
+ * There is a 1:1 mapping between 'mTimelines' and 'mEventTimes'.
+ * We are using 'multimap' instead of 'map' because there could be more than 1 event with the
+ * same eventTime.
+ */
+ std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes;
+
+ InputEventTimelineProcessor* mTimelineProcessor;
+ void reportAndPruneMatureRecords(nsecs_t newEventTime);
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index b601dfc..7f85e53 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -151,6 +151,14 @@
*/
virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop) = 0;
+
+ /**
+ * Transfer touch focus to the provided channel, no matter where the current touch is.
+ *
+ * Return true on success, false if there was no on-going touch.
+ */
+ virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0;
+
/**
* Sets focus on the specified window.
*/
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 3bf212a..19abfd9 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -198,6 +198,10 @@
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, uint8_t> portAssociations;
+ // The associations between input device names and display unique ids.
+ // Used to determine which DisplayViewport should be tied to which InputDevice.
+ std::unordered_map<std::string, std::string> uniqueIdAssociations;
+
// The suggested display ID to show the cursor.
int32_t defaultPointerDisplayId;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 8f75d22..ad503fd 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -103,6 +103,12 @@
} else {
dump += "<none>\n";
}
+ dump += StringPrintf(INDENT2 "AssociatedDisplayUniqueId: ");
+ if (mAssociatedDisplayUniqueId) {
+ dump += StringPrintf("%s\n", mAssociatedDisplayUniqueId->c_str());
+ } else {
+ dump += "<none>\n";
+ }
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -293,8 +299,9 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- // In most situations, no port will be specified.
+ // In most situations, no port or name will be specified.
mAssociatedDisplayPort = std::nullopt;
+ mAssociatedDisplayUniqueId = std::nullopt;
mAssociatedViewport = std::nullopt;
// Find the display port that corresponds to the current input port.
const std::string& inputPort = mIdentifier.location;
@@ -305,6 +312,13 @@
mAssociatedDisplayPort = std::make_optional(displayPort->second);
}
}
+ const std::string& inputDeviceName = mIdentifier.name;
+ const std::unordered_map<std::string, std::string>& names =
+ config->uniqueIdAssociations;
+ const auto& displayUniqueId = names.find(inputDeviceName);
+ if (displayUniqueId != names.end()) {
+ mAssociatedDisplayUniqueId = displayUniqueId->second;
+ }
// If the device was explicitly disabled by the user, it would be present in the
// "disabledDevices" list. If it is associated with a specific display, and it was not
@@ -319,6 +333,15 @@
getName().c_str(), *mAssociatedDisplayPort);
enabled = false;
}
+ } else if (mAssociatedDisplayUniqueId != std::nullopt) {
+ mAssociatedViewport =
+ config->getDisplayViewportByUniqueId(*mAssociatedDisplayUniqueId);
+ if (!mAssociatedViewport) {
+ ALOGW("Input device %s should be associated with display %s but the "
+ "corresponding viewport cannot be found",
+ inputDeviceName.c_str(), mAssociatedDisplayUniqueId->c_str());
+ enabled = false;
+ }
}
if (changes) {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 2d0fdf7..e91f84e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -413,8 +413,7 @@
}
}
-void InputReader::dispatchExternalStylusState(const StylusState& state) {
- std::scoped_lock _l(mLock);
+void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
device->updateExternalStylusState(state);
@@ -945,7 +944,7 @@
}
void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
- mReader->dispatchExternalStylusState(state);
+ mReader->dispatchExternalStylusStateLocked(state);
}
InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 1a40d06..16251ee 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -104,7 +104,7 @@
context.setLightBrightness(rawLightId, brightness);
}
-bool PeripheralController::SingleLight::setLightColor(int32_t color) {
+bool PeripheralController::MonoLight::setLightColor(int32_t color) {
int32_t brightness = getAlpha(color);
setRawLightBrightness(rawId, brightness);
@@ -148,7 +148,7 @@
return true;
}
-std::optional<int32_t> PeripheralController::SingleLight::getLightColor() {
+std::optional<int32_t> PeripheralController::MonoLight::getLightColor() {
std::optional<int32_t> brightness = getRawLightBrightness(rawId);
if (!brightness.has_value()) {
return std::nullopt;
@@ -234,7 +234,7 @@
return std::nullopt;
}
-void PeripheralController::SingleLight::dump(std::string& dump) {
+void PeripheralController::MonoLight::dump(std::string& dump) {
dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
}
@@ -423,7 +423,7 @@
playerIdLightIds);
mLights.insert_or_assign(light->id, std::move(light));
// Remove these raw lights from raw light info as they've been used to compose a
- // Player ID light, so we do not expose these raw lights as single lights.
+ // Player ID light, so we do not expose these raw lights as mono lights.
for (const auto& [playerId, rawId] : playerIdLightIds) {
rawInfos.erase(rawId);
}
@@ -460,13 +460,12 @@
mLights.insert_or_assign(light->id, std::move(light));
continue;
}
- // Construct a single LED light
+ // Construct a Mono LED light
if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+ ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
}
- std::unique_ptr<Light> light =
- std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
- rawInfo.id);
+ std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
+ ++mNextId, rawInfo.id);
mLights.insert_or_assign(light->id, std::move(light));
}
diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h
index ff3607f..b1bc8c7 100644
--- a/services/inputflinger/reader/controller/PeripheralController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -78,10 +78,10 @@
void setRawLightBrightness(int32_t rawLightId, int32_t brightness);
};
- struct SingleLight : public Light {
- explicit SingleLight(InputDeviceContext& context, const std::string& name, int32_t id,
- int32_t rawId)
- : Light(context, name, id, InputDeviceLightType::SINGLE), rawId(rawId) {}
+ struct MonoLight : public Light {
+ explicit MonoLight(InputDeviceContext& context, const std::string& name, int32_t id,
+ int32_t rawId)
+ : Light(context, name, id, InputDeviceLightType::MONO), rawId(rawId) {}
int32_t rawId;
bool setLightColor(int32_t color) override;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index b2b23e5..291f105 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -169,6 +169,7 @@
uint32_t mSources;
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
+ std::optional<std::string> mAssociatedDisplayUniqueId;
std::optional<DisplayViewport> mAssociatedViewport;
bool mHasMic;
bool mDropUntilNextSync;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1405671..bc79ccf 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -203,7 +203,7 @@
void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
- void dispatchExternalStylusState(const StylusState& state);
+ void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock);
// The PointerController that is shared among all the input devices that need it.
std::weak_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 2c5a576..104d087 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -132,9 +132,7 @@
std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
nsecs_t when, const InputReaderConfiguration* config) {
- const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
- if (displayPort) {
- // Find the viewport that contains the same port
+ if (getDeviceContext().getAssociatedViewport()) {
return getDeviceContext().getAssociatedViewport();
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 09e1c05..f8d9a8d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2465,6 +2465,12 @@
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
+ uint32_t flags = 0;
+
+ if (!PointerGesture::canGestureAffectWindowFocus(mPointerGesture.currentGestureMode)) {
+ flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ }
+
// Update last coordinates of pointers that have moved so that we observe the new
// pointer positions at the same time as other pointers that have just gone up.
bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP ||
@@ -2494,8 +2500,8 @@
BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
if (!dispatchedGestureIdBits.isEmpty()) {
if (cancelPreviousGesture) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0,
+ flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
mPointerGesture.downTime);
@@ -2513,7 +2519,7 @@
uint32_t id = upGestureIdBits.clearFirstMarkedBit();
dispatchMotion(when, readTime, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState,
AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2526,7 +2532,7 @@
// Send motion events for all pointers that moved.
if (moveNeeded) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags,
metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
@@ -2547,7 +2553,7 @@
}
dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
- 0, 0, metaState, buttonState, 0,
+ 0, flags, metaState, buttonState, 0,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2557,8 +2563,8 @@
// Send motion events for hover.
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+ flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
@@ -2582,7 +2588,7 @@
const int32_t displayId = mPointerController->getDisplayId();
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 5146299..920f842 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -590,6 +590,27 @@
QUIET,
};
+ // When a gesture is sent to an unfocused window, return true if it can bring that window
+ // into focus, false otherwise.
+ static bool canGestureAffectWindowFocus(Mode mode) {
+ switch (mode) {
+ case Mode::TAP:
+ case Mode::TAP_DRAG:
+ case Mode::BUTTON_CLICK_OR_DRAG:
+ // Taps can affect window focus.
+ return true;
+ case Mode::FREEFORM:
+ case Mode::HOVER:
+ case Mode::NEUTRAL:
+ case Mode::PRESS:
+ case Mode::QUIET:
+ case Mode::SWIPE:
+ // Most gestures can be performed on an unfocused window, so they should not
+ // not affect window focus.
+ return false;
+ }
+ }
+
// Time the first finger went down.
nsecs_t firstTouchTime;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 42b54c7..918e1be 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -46,6 +46,7 @@
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
"InputFlingerService_test.cpp",
+ "LatencyTracker_test.cpp",
"TestInputListener.cpp",
"UinputDevice.cpp",
],
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 31d6900..93aa6ac 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -441,7 +441,7 @@
sp<FakeInputDispatcherPolicy> mFakePolicy;
sp<InputDispatcher> mDispatcher;
- virtual void SetUp() override {
+ void SetUp() override {
mFakePolicy = new FakeInputDispatcherPolicy();
mDispatcher = new InputDispatcher(mFakePolicy);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
@@ -449,7 +449,7 @@
ASSERT_EQ(OK, mDispatcher->start());
}
- virtual void TearDown() override {
+ void TearDown() override {
ASSERT_EQ(OK, mDispatcher->stop());
mFakePolicy.clear();
mDispatcher.clear();
@@ -526,7 +526,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
/*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -539,6 +540,7 @@
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -551,6 +553,7 @@
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -564,6 +567,7 @@
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -576,6 +580,7 @@
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -587,7 +592,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -597,7 +603,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -609,7 +616,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -620,7 +628,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -633,7 +642,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -1215,6 +1225,11 @@
return *this;
}
+ MotionEventBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
MotionEvent build() {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
@@ -1234,11 +1249,11 @@
MotionEvent event;
ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
- mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
mButtonState, MotionClassification::NONE, identityTransform,
/* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
- mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(),
- pointerProperties.data(), pointerCoords.data());
+ mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime,
+ mPointers.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
@@ -1250,8 +1265,11 @@
int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
int32_t mActionButton{0};
int32_t mButtonState{0};
+ int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ int32_t mDisplayWidth{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+ int32_t mDisplayHeight{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
std::vector<PointerBuilder> mPointers;
};
@@ -1696,7 +1714,13 @@
0 /*expectedFlags*/);
}
-TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+using TransferFunction =
+ std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>;
+
+class TransferTouchFixture : public InputDispatcherTest,
+ public ::testing::WithParamInterface<TransferFunction> {};
+
+TEST_P(TransferTouchFixture, TransferTouch_OnePointer) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a couple of windows
@@ -1717,8 +1741,10 @@
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
- // Transfer touch focus to the second window
- mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ // Transfer touch to the second window
+ TransferFunction f = GetParam();
+ const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+ ASSERT_TRUE(success);
// The first window gets cancel and the second gets down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown();
@@ -1733,7 +1759,7 @@
secondWindow->consumeMotionUp();
}
-TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
PointF touchPoint = {10, 10};
@@ -1768,7 +1794,9 @@
secondWindow->assertNoEvents();
// Transfer touch focus to the second window
- mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ TransferFunction f = GetParam();
+ bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+ ASSERT_TRUE(success);
// The first window gets cancel and the second gets down and pointer down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown();
@@ -1795,6 +1823,21 @@
secondWindow->consumeMotionUp();
}
+// For the cases of single pointer touch and two pointers non-split touch, the api's
+// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ
+// for the case where there are multiple pointers split across several windows.
+INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
+ ::testing::Values(
+ [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/,
+ sp<IBinder> destChannelToken) {
+ return dispatcher->transferTouch(destChannelToken);
+ },
+ [&](sp<InputDispatcher> dispatcher, sp<IBinder> from,
+ sp<IBinder> to) {
+ return dispatcher->transferTouchFocus(from, to,
+ false /*isDragAndDrop*/);
+ }));
+
TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -1865,6 +1908,82 @@
secondWindow->consumeMotionUp();
}
+// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api.
+// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving
+// touch is not supported, so the touch should continue on those windows and the transferred-to
+// window should get nothing.
+TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ firstWindow->setFrame(Rect(0, 0, 600, 400));
+ firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+ secondWindow->setFrame(Rect(0, 400, 600, 800));
+ secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+ PointF pointInFirst = {300, 200};
+ PointF pointInSecond = {300, 600};
+
+ // Send down to the first window
+ NotifyMotionArgs firstDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInFirst});
+ mDispatcher->notifyMotion(&firstDownMotionArgs);
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+
+ // Send down to the second window
+ NotifyMotionArgs secondDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&secondDownMotionArgs);
+ // The first window gets a move and the second a down
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionDown();
+
+ // Transfer touch focus to the second window
+ const bool transferred = mDispatcher->transferTouch(secondWindow->getToken());
+ // The 'transferTouch' call should not succeed, because there are 2 touched windows
+ ASSERT_FALSE(transferred);
+ firstWindow->assertNoEvents();
+ secondWindow->assertNoEvents();
+
+ // The rest of the dispatch should proceed as normal
+ // Send pointer up to the second window
+ NotifyMotionArgs pointerUpMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&pointerUpMotionArgs);
+ // The first window gets MOVE and the second gets pointer up
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionUp();
+
+ // Send up event to the first window
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets nothing and the second gets up
+ firstWindow->consumeMotionUp();
+ secondWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -2029,6 +2148,19 @@
expectedDisplayId, expectedFlags);
}
+ MotionEvent* consumeMotion() {
+ InputEvent* event = mInputReceiver->consume();
+ if (!event) {
+ ADD_FAILURE() << "No event was produced";
+ return nullptr;
+ }
+ if (event->getType() != AINPUT_EVENT_TYPE_MOTION) {
+ ADD_FAILURE() << "Received event of type " << event->getType() << " instead of motion";
+ return nullptr;
+ }
+ return static_cast<MotionEvent*>(event);
+ }
+
void assertNoEvents() { mInputReceiver->assertNoEvents(); }
private:
@@ -2115,6 +2247,27 @@
mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
}
+// Tests for gesture monitors
+TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ window->setWindowOffset(20, 40);
+ window->setWindowTransform(0, 1, -1, 0);
+
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ MotionEvent* event = monitor.consumeMotion();
+ // Even though window has transform, gesture monitor must not.
+ ASSERT_EQ(ui::Transform(), event->getTransform());
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -3059,6 +3212,25 @@
mFakePolicy->assertOnPointerDownWasNotCalled();
}
+// Have two windows, one with focus. Injecting a trusted DOWN MotionEvent with the flag
+// NO_FOCUS_CHANGE on the unfocused window should not call the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, NoFocusChangeFlag) {
+ const MotionEvent event =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(20).y(20))
+ .addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
+ // Ensure that the unfocused window did not receive any FOCUS events.
+ mUnfocusedWindow->assertNoEvents();
+}
+
// These tests ensures we can send touch events to a single client when there are multiple input
// windows that point to the same client token.
class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 0e721e9..46923ca 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -269,6 +269,11 @@
mConfig.portAssociations.insert({inputPort, displayPort});
}
+ void addInputUniqueIdAssociation(const std::string& inputUniqueId,
+ const std::string& displayUniqueId) {
+ mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+ }
+
void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
@@ -2621,6 +2626,41 @@
ASSERT_FALSE(mDevice->isEnabled());
}
+TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) {
+ // Device should be enabled by default.
+ mFakePolicy->clearViewports();
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ ASSERT_TRUE(mDevice->isEnabled());
+
+ // Device should be disabled because it is associated with a specific display, but the
+ // corresponding display is not found.
+ const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_NAME, DISPLAY_UNIQUE_ID);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_FALSE(mDevice->isEnabled());
+
+ // Device should be enabled when a display is found.
+ mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ NO_PORT, ViewportType::INTERNAL);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_TRUE(mDevice->isEnabled());
+
+ // Device should be disabled after set disable.
+ mFakePolicy->addDisabledDevice(mDevice->getId());
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ ASSERT_FALSE(mDevice->isEnabled());
+
+ // Device should still be disabled even found the associated display.
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_FALSE(mDevice->isEnabled());
+}
+
// --- InputMapperTest ---
class InputMapperTest : public testing::Test {
@@ -7853,8 +7893,6 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
}
-
-
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -8705,20 +8743,20 @@
}
};
-TEST_F(LightControllerTest, SingleLight) {
- RawLightInfo infoSingle = {.id = 1,
- .name = "Mono",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS,
- .path = ""};
- mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
+TEST_F(LightControllerTest, MonoLight) {
+ RawLightInfo infoMono = {.id = 1,
+ .name = "Mono",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(infoMono.id, std::move(infoMono));
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
InputDeviceInfo info;
controller.populateDeviceInfo(&info);
const auto& ids = info.getLightIds();
ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
+ ASSERT_EQ(InputDeviceLightType::MONO, info.getLightInfo(ids[0])->type);
ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_BRIGHTNESS));
ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
new file mode 100644
index 0000000..e7e1937
--- /dev/null
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -0,0 +1,253 @@
+/*
+ * 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 "../dispatcher/LatencyTracker.h"
+
+#include <binder/Binder.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+#define TAG "LatencyTracker_test"
+
+using android::inputdispatcher::InputEventTimeline;
+using android::inputdispatcher::LatencyTracker;
+
+namespace android::inputdispatcher {
+
+InputEventTimeline getTestTimeline() {
+ InputEventTimeline t(
+ /*isDown*/ true,
+ /*eventTime*/ 2,
+ /*readTime*/ 3);
+ ConnectionTimeline expectedCT(/*deliveryTime*/ 6, /* consumeTime*/ 7, /*finishTime*/ 8);
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 10;
+ expectedCT.setGraphicsTimeline(std::move(graphicsTimeline));
+ t.connectionTimelines.emplace(new BBinder(), std::move(expectedCT));
+ return t;
+}
+
+// --- LatencyTrackerTest ---
+class LatencyTrackerTest : public testing::Test, public InputEventTimelineProcessor {
+protected:
+ std::unique_ptr<LatencyTracker> mTracker;
+ sp<IBinder> connection1;
+ sp<IBinder> connection2;
+
+ void SetUp() override {
+ connection1 = new BBinder();
+ connection2 = new BBinder();
+
+ mTracker = std::make_unique<LatencyTracker>(this);
+ }
+ void TearDown() override {}
+
+ void assertReceivedTimeline(const InputEventTimeline& timeline);
+ /**
+ * Timelines can be received in any order (order is not guaranteed). So if we are expecting more
+ * than 1 timeline, use this function to check that the set of received timelines matches
+ * what we expected.
+ */
+ void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines);
+
+private:
+ void processTimeline(const InputEventTimeline& timeline) override {
+ mReceivedTimelines.push_back(timeline);
+ }
+ std::deque<InputEventTimeline> mReceivedTimelines;
+};
+
+void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
+ mTracker->reportNow();
+ ASSERT_FALSE(mReceivedTimelines.empty());
+ const InputEventTimeline& t = mReceivedTimelines.front();
+ ASSERT_EQ(timeline, t);
+ mReceivedTimelines.pop_front();
+}
+
+/**
+ * We are essentially comparing two multisets, but without constructing them.
+ * This comparison is inefficient, but it avoids having to construct a set, and also avoids the
+ * declaration of copy constructor for ConnectionTimeline.
+ * We ensure that collections A and B have the same size, that for every element in A, there is an
+ * equal element in B, and for every element in B there is an equal element in A.
+ */
+void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) {
+ mTracker->reportNow();
+ ASSERT_EQ(timelines.size(), mReceivedTimelines.size());
+ for (const InputEventTimeline& expectedTimeline : timelines) {
+ bool found = false;
+ for (const InputEventTimeline& receivedTimeline : mReceivedTimelines) {
+ if (receivedTimeline == expectedTimeline) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found) << "Could not find expected timeline with eventTime="
+ << expectedTimeline.eventTime;
+ }
+ for (const InputEventTimeline& receivedTimeline : mReceivedTimelines) {
+ bool found = false;
+ for (const InputEventTimeline& expectedTimeline : timelines) {
+ if (receivedTimeline == expectedTimeline) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found) << "Could not find received timeline with eventTime="
+ << receivedTimeline.eventTime;
+ }
+ mReceivedTimelines.clear();
+}
+
+/**
+ * Ensure that calling 'trackListener' in isolation only creates an inputflinger timeline, without
+ * any additional ConnectionTimeline's.
+ */
+TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
+ mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/);
+ assertReceivedTimeline(InputEventTimeline{false, 2, 3});
+}
+
+/**
+ * A single call to trackFinishedEvent should not cause a timeline to be reported.
+ */
+TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) {
+ mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/,
+ 3 /*consumeTime*/, 4 /*finishTime*/);
+ assertReceivedTimelines({});
+}
+
+/**
+ * A single call to trackGraphicsLatency should not cause a timeline to be reported.
+ */
+TEST_F(LatencyTrackerTest, TrackGraphicsLatency_DoesNotTriggerReporting) {
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
+ mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline);
+ assertReceivedTimelines({});
+}
+
+TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) {
+ constexpr int32_t inputEventId = 1;
+ InputEventTimeline expected = getTestTimeline();
+
+ const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin();
+
+ mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+ mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime,
+ expectedCT.consumeTime, expectedCT.finishTime);
+ mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
+
+ assertReceivedTimeline(expected);
+}
+
+TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) {
+ constexpr int32_t inputEventId1 = 1;
+ InputEventTimeline timeline1(
+ /*isDown*/ true,
+ /*eventTime*/ 2,
+ /*readTime*/ 3);
+ timeline1.connectionTimelines.emplace(connection1,
+ ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7,
+ /*finishTime*/ 8));
+ ConnectionTimeline& connectionTimeline1 = timeline1.connectionTimelines.begin()->second;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline1;
+ graphicsTimeline1[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
+ graphicsTimeline1[GraphicsTimeline::PRESENT_TIME] = 10;
+ connectionTimeline1.setGraphicsTimeline(std::move(graphicsTimeline1));
+
+ constexpr int32_t inputEventId2 = 10;
+ InputEventTimeline timeline2(
+ /*isDown*/ false,
+ /*eventTime*/ 20,
+ /*readTime*/ 30);
+ timeline2.connectionTimelines.emplace(connection2,
+ ConnectionTimeline(/*deliveryTime*/ 60,
+ /*consumeTime*/ 70,
+ /*finishTime*/ 80));
+ ConnectionTimeline& connectionTimeline2 = timeline2.connectionTimelines.begin()->second;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline2;
+ graphicsTimeline2[GraphicsTimeline::GPU_COMPLETED_TIME] = 90;
+ graphicsTimeline2[GraphicsTimeline::PRESENT_TIME] = 100;
+ connectionTimeline2.setGraphicsTimeline(std::move(graphicsTimeline2));
+
+ // Start processing first event
+ mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime,
+ timeline1.readTime);
+ // Start processing second event
+ mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime,
+ timeline2.readTime);
+ mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime,
+ connectionTimeline1.consumeTime, connectionTimeline1.finishTime);
+
+ mTracker->trackFinishedEvent(inputEventId2, connection2, connectionTimeline2.deliveryTime,
+ connectionTimeline2.consumeTime, connectionTimeline2.finishTime);
+ mTracker->trackGraphicsLatency(inputEventId1, connection1,
+ connectionTimeline1.graphicsTimeline);
+ mTracker->trackGraphicsLatency(inputEventId2, connection2,
+ connectionTimeline2.graphicsTimeline);
+ // Now both events should be completed
+ assertReceivedTimelines({timeline1, timeline2});
+}
+
+/**
+ * Check that LatencyTracker consistently tracks events even if there are many incomplete events.
+ */
+TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) {
+ InputEventTimeline timeline = getTestTimeline();
+ std::vector<InputEventTimeline> expectedTimelines;
+ const ConnectionTimeline& expectedCT = timeline.connectionTimelines.begin()->second;
+ const sp<IBinder>& token = timeline.connectionTimelines.begin()->first;
+
+ for (size_t i = 1; i <= 100; i++) {
+ mTracker->trackListener(i /*inputEventId*/, timeline.isDown, timeline.eventTime,
+ timeline.readTime);
+ expectedTimelines.push_back(
+ InputEventTimeline{timeline.isDown, timeline.eventTime, timeline.readTime});
+ }
+ // Now, complete the first event that was sent.
+ mTracker->trackFinishedEvent(1 /*inputEventId*/, token, expectedCT.deliveryTime,
+ expectedCT.consumeTime, expectedCT.finishTime);
+ mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline);
+
+ expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT));
+ assertReceivedTimelines(expectedTimelines);
+}
+
+/**
+ * For simplicity of the implementation, LatencyTracker only starts tracking an event when
+ * 'trackListener' is invoked.
+ * Both 'trackFinishedEvent' and 'trackGraphicsLatency' should not start a new event.
+ * If they are received before 'trackListener' (which should not be possible), they are ignored.
+ */
+TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) {
+ constexpr int32_t inputEventId = 1;
+ InputEventTimeline expected = getTestTimeline();
+ const ConnectionTimeline& expectedCT = expected.connectionTimelines.begin()->second;
+ mTracker->trackFinishedEvent(inputEventId, connection1, expectedCT.deliveryTime,
+ expectedCT.consumeTime, expectedCT.finishTime);
+ mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
+
+ mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+ assertReceivedTimeline(
+ InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime});
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index d8e8b52..5d6f8c7 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -132,8 +132,6 @@
}
void SensorDevice::initializeSensorList() {
- float minPowerMa = 0.001; // 1 microAmp
-
checkReturn(mSensors->getSensorsList(
[&](const auto &list) {
const size_t count = list.size();
@@ -151,13 +149,18 @@
// 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) {
- double promotedResolution = sensor.resolution;
- double promotedMaxRange = sensor.maxRange;
- if (fmod(promotedMaxRange, promotedResolution) != 0) {
- ALOGW("%s's max range %f is not a multiple of the resolution %f",
- sensor.name, sensor.maxRange, sensor.resolution);
- SensorDeviceUtils::quantizeValue(
- &sensor.maxRange, promotedResolution);
+ 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.
@@ -166,10 +169,11 @@
}
// Sanity check and clamp power if it is 0 (or close)
- if (sensor.power < minPowerMa) {
- ALOGI("Reported power %f not deemed sane, clamping to %f",
- sensor.power, minPowerMa);
- sensor.power = minPowerMa;
+ 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);
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index 1309971..255f7e1 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -32,16 +32,15 @@
namespace android {
namespace SensorDeviceUtils {
-// Quantizes a single value using a sensor's resolution.
-inline void quantizeValue(float *value, double resolution) {
+// Quantizes a single value to (a fractional factor of) a sensor's resolution. Typically we
+// increase the value of the sensor's nominal resolution to ensure that sensor accuracy
+// improvements, like runtime calibration, are not masked during requantization.
+inline void quantizeValue(float *value, double resolution, double factor = 0.125) {
if (resolution == 0) {
return;
}
- // Increase the value of the sensor's nominal resolution to ensure that
- // sensor accuracy improvements, like runtime calibration, are not masked
- // during requantization.
- double incRes = 0.125 * resolution;
+ double incRes = factor * resolution;
*value = round(static_cast<double>(*value) / incRes) * incRes;
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 9955cdb..f949196 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2138,7 +2138,12 @@
void SensorService::SensorPrivacyPolicy::unregisterSelf() {
AutoCallerClear acc;
SensorPrivacyManager spm;
- spm.removeSensorPrivacyListener(this);
+ if (mIsIndividualMic) {
+ spm.removeIndividualSensorPrivacyListener(
+ SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this);
+ } else {
+ spm.removeSensorPrivacyListener(this);
+ }
}
bool SensorService::SensorPrivacyPolicy::isSensorPrivacyEnabled() {
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml
index 5fd361c..c564b7b 100644
--- a/services/stats/android.frameworks.stats@1.0-service.xml
+++ b/services/stats/android.frameworks.stats@1.0-service.xml
@@ -1,5 +1,5 @@
<manifest version="1.0" type="framework">
- <hal>
+ <hal max-level="5">
<name>android.frameworks.stats</name>
<transport>hwbinder</transport>
<version>1.0</version>
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9885352..f20bfe1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -25,7 +25,7 @@
name: "libsurfaceflinger_defaults",
defaults: [
"surfaceflinger_defaults",
- "skia_deps",
+ "skia_renderengine_deps",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d243989..cacad52 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -58,8 +58,7 @@
namespace android {
-static constexpr float defaultMaxMasteringLuminance = 1000.0;
-static constexpr float defaultMaxContentLuminance = 1000.0;
+static constexpr float defaultMaxLuminance = 1000.0;
BufferLayer::BufferLayer(const LayerCreationArgs& args)
: Layer(args),
@@ -186,7 +185,7 @@
}
}
const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
- (isSecure() && !targetSettings.isSecure);
+ ((isSecure() || isProtected()) && !targetSettings.isSecure);
const bool bufferCanBeUsedAsHwTexture =
mBufferInfo.mBuffer->getBuffer()->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
compositionengine::LayerFE::LayerSettings& layer = *result;
@@ -206,12 +205,24 @@
layer.source.buffer.isY410BT2020 = isHdrY410();
bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
- layer.source.buffer.maxMasteringLuminance = hasSmpte2086
- ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
- : defaultMaxMasteringLuminance;
- layer.source.buffer.maxContentLuminance = hasCta861_3
- ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
- : defaultMaxContentLuminance;
+ float maxLuminance = 0.f;
+ if (hasSmpte2086 && hasCta861_3) {
+ maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
+ mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
+ } else if (hasSmpte2086) {
+ maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
+ } else if (hasCta861_3) {
+ maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
+ } else {
+ switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // Behavior-match previous releases for HDR content
+ maxLuminance = defaultMaxLuminance;
+ break;
+ }
+ }
+ layer.source.buffer.maxLuminanceNits = maxLuminance;
layer.frameNumber = mCurrentFrameNumber;
layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()->getId() : 0;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index ac2edbe..54daa10 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -284,12 +284,83 @@
return true;
}
+bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) {
+ if (mCurrentState.bufferCrop == bufferCrop) return false;
+
+ mCurrentState.sequence++;
+ mCurrentState.bufferCrop = bufferCrop;
+
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) {
+ if (mCurrentState.destinationFrame == destinationFrame) return false;
+
+ mCurrentState.sequence++;
+ mCurrentState.destinationFrame = destinationFrame;
+
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+// Translate destination frame into scale and position. If a destination frame is not set, use the
+// provided scale and position
+void BufferStateLayer::updateGeometry() {
+ if (mCurrentState.destinationFrame.isEmpty()) {
+ // If destination frame is not set, use the requested transform set via
+ // BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
+ mCurrentState.transform = mRequestedTransform;
+ return;
+ }
+
+ Rect destRect = mCurrentState.destinationFrame;
+ int32_t destW = destRect.width();
+ int32_t destH = destRect.height();
+ if (destRect.left < 0) {
+ destRect.left = 0;
+ destRect.right = destW;
+ }
+ if (destRect.top < 0) {
+ destRect.top = 0;
+ destRect.bottom = destH;
+ }
+
+ if (!mCurrentState.buffer) {
+ ui::Transform t;
+ t.set(destRect.left, destRect.top);
+ mCurrentState.transform = t;
+ return;
+ }
+
+ uint32_t bufferWidth = mCurrentState.buffer->getBuffer()->getWidth();
+ uint32_t bufferHeight = mCurrentState.buffer->getBuffer()->getHeight();
+ // Undo any transformations on the buffer.
+ if (mCurrentState.bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (mCurrentState.transformToDisplayInverse) {
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ float sx = destW / static_cast<float>(bufferWidth);
+ float sy = destH / static_cast<float>(bufferHeight);
+ ui::Transform t;
+ t.set(sx, 0, 0, sy);
+ t.set(destRect.left, destRect.top);
+ mCurrentState.transform = t;
+ return;
+}
+
bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
- if (mCurrentState.transform.dsdx() == matrix.dsdx &&
- mCurrentState.transform.dtdy() == matrix.dtdy &&
- mCurrentState.transform.dtdx() == matrix.dtdx &&
- mCurrentState.transform.dsdy() == matrix.dsdy) {
+ if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
+ mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
return false;
}
@@ -302,7 +373,7 @@
return false;
}
- mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+ mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
mCurrentState.sequence++;
mCurrentState.modified = true;
@@ -312,11 +383,11 @@
}
bool BufferStateLayer::setPosition(float x, float y) {
- if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) {
+ if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
return false;
}
- mCurrentState.transform.set(x, y);
+ mRequestedTransform.set(x, y);
mCurrentState.sequence++;
mCurrentState.modified = true;
@@ -512,35 +583,35 @@
Rect BufferStateLayer::getBufferSize(const State& s) const {
// for buffer state layers we use the display frame size as the buffer size.
- if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
- return Rect(getActiveWidth(s), getActiveHeight(s));
- }
if (mBufferInfo.mBuffer == nullptr) {
return Rect::INVALID_RECT;
}
- // if the display frame is not defined, use the parent bounds as the buffer size.
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- Rect parentBounds = Rect(p->getBounds(Region()));
- if (!parentBounds.isEmpty()) {
- return parentBounds;
+ uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (getTransformToDisplayInverse()) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
}
}
- return Rect::INVALID_RECT;
+ return Rect(0, 0, bufWidth, bufHeight);
}
FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
- const State& s(getDrawingState());
- // for buffer state layers we use the display frame size as the buffer size.
- if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
- return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ if (mBufferInfo.mBuffer == nullptr) {
+ return parentBounds;
}
- // if the display frame is not defined, use the parent bounds as the buffer size.
- return parentBounds;
+ return getBufferSize(getDrawingState()).toFloatRect();
}
// -----------------------------------------------------------------------
@@ -611,7 +682,10 @@
}
bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
- if (mSidebandStreamChanged.exchange(false)) {
+ // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+ const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+
+ if (mSidebandStreamChanged.exchange(false) || updateSidebandStream) {
const State& s(getDrawingState());
// mSidebandStreamChanged was true
mSidebandStream = s.sidebandStream;
@@ -674,6 +748,11 @@
latchTime);
}
+ std::deque<sp<CallbackHandle>> remainingHandles;
+ mFlinger->getTransactionCallbackInvoker()
+ .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+ mDrawingState.callbackHandles = remainingHandles;
+
mCurrentStateModified = false;
return NO_ERROR;
@@ -790,7 +869,7 @@
mBufferInfo.mFence = s.acquireFence;
mBufferInfo.mTransform = s.bufferTransform;
mBufferInfo.mDataspace = translateDataspace(s.dataspace);
- mBufferInfo.mCrop = computeCrop(s);
+ mBufferInfo.mCrop = computeBufferCrop(s);
mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
mBufferInfo.mHdrMetadata = s.hdrMetadata;
@@ -803,27 +882,16 @@
return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
}
-Rect BufferStateLayer::computeCrop(const State& s) {
- if (s.crop.isEmpty() && s.buffer) {
- return s.buffer->getBuffer()->getBounds();
+Rect BufferStateLayer::computeBufferCrop(const State& s) {
+ if (s.buffer && !s.bufferCrop.isEmpty()) {
+ Rect bufferCrop;
+ s.buffer->getBuffer()->getBounds().intersect(s.bufferCrop, &bufferCrop);
+ return bufferCrop;
} else if (s.buffer) {
- Rect crop = s.crop;
- crop.left = std::max(crop.left, 0);
- crop.top = std::max(crop.top, 0);
- uint32_t bufferWidth = s.buffer->getBuffer()->getWidth();
- uint32_t bufferHeight = s.buffer->getBuffer()->getHeight();
- if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
- bufferWidth <= std::numeric_limits<int32_t>::max()) {
- crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
- crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
- }
- if (!crop.isValid()) {
- // Crop rect is out of bounds, return whole buffer
- return s.buffer->getBuffer()->getBounds();
- }
- return crop;
+ return s.buffer->getBuffer()->getBounds();
+ } else {
+ return s.bufferCrop;
}
- return s.crop;
}
sp<Layer> BufferStateLayer::createClone() {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 4171092..2e48452 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -51,9 +51,6 @@
return flags;
}
- uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
- uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
- ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
}
@@ -88,6 +85,10 @@
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
void setAutoRefresh(bool autoRefresh) override;
+ bool setBufferCrop(const Rect& bufferCrop) override;
+ bool setDestinationFrame(const Rect& destinationFrame) override;
+ void updateGeometry() override;
+
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
@@ -140,7 +141,7 @@
sp<Layer> createClone() override;
// Crop that applies to the buffer
- Rect computeCrop(const State& s);
+ Rect computeBufferCrop(const State& s);
bool willPresentCurrentTransaction() const;
@@ -179,6 +180,10 @@
// - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
std::atomic<int32_t> mPendingBufferTransactions{0};
+ // Contains requested position and matrix updates. This will be applied if the client does
+ // not specify a destination frame.
+ ui::Transform mRequestedTransform;
+
// TODO(marissaw): support sticky transform for LEGACY camera mode
class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/Clock.h b/services/surfaceflinger/Clock.h
new file mode 100644
index 0000000..3f23c6d
--- /dev/null
+++ b/services/surfaceflinger/Clock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+namespace android {
+
+// Abstract interface for timekeeping which can be injected for unit tests.
+class Clock {
+public:
+ Clock() = default;
+ virtual ~Clock() = default;
+
+ // Returns the current time
+ virtual std::chrono::steady_clock::time_point now() const = 0;
+};
+
+class SteadyClock : public Clock {
+public:
+ virtual ~SteadyClock() = default;
+
+ std::chrono::steady_clock::time_point now() const override {
+ return std::chrono::steady_clock::now();
+ }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index a0606b4..289cb11 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -79,6 +79,9 @@
// If set, causes the dirty regions to flash with the delay
std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
+
+ // The earliest time to send the present command to the HAL
+ std::chrono::steady_clock::time_point earliestPresentTime;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 1fd07b0..791e7db 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -138,6 +138,9 @@
// Gets the sequence number: a serial number that uniquely identifies a Layer
virtual int32_t getSequence() const = 0;
+
+ // Whether the layer should be rendered with rounded corners.
+ virtual bool hasRoundedCorners() const = 0;
};
// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 4976213..257974f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,6 +163,9 @@
// Enables (or disables) composition on this output
virtual void setCompositionEnabled(bool) = 0;
+ // Enables (or disables) layer caching on this output
+ virtual void setLayerCachingEnabled(bool) = 0;
+
// Sets the projection state to use
virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) = 0;
@@ -181,6 +184,9 @@
// Sets the output color mode
virtual void setColorProfile(const ColorProfile&) = 0;
+ // Sets current calibrated display brightness information
+ virtual void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) = 0;
+
// Outputs a string with a state dump
virtual void dump(std::string&) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 3a84327..ead941d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <cstdint>
#include <optional>
#include <string>
@@ -90,8 +91,13 @@
// Writes the geometry state to the HWC, or does nothing if this layer does
// not use the HWC. If includeGeometry is false, the geometry state can be
// skipped. If skipLayer is true, then the alpha of the layer is forced to
- // 0 so that HWC will ignore it.
- virtual void writeStateToHWC(bool includeGeometry, bool skipLayer) = 0;
+ // 0 so that HWC will ignore it. z specifies the order to draw the layer in
+ // (starting with 0 for the back layer, and increasing for each following
+ // layer). zIsOverridden specifies whether the layer has been reordered.
+ // isPeekingThrough specifies whether this layer will be shown through a
+ // hole punch in a layer above it.
+ virtual void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
+ bool zIsOverridden, bool isPeekingThrough) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 7ca91d8..58bb41a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -96,6 +96,11 @@
// physical translation and finally rotate to the physical orientation.
return rotationTransform * destTranslation * scale * sourceTranslation;
}
+
+ bool operator==(const ProjectionSpace& other) const {
+ return bounds == other.bounds && content == other.content &&
+ orientation == other.orientation;
+ }
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index aa049a8..fd22aa3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -57,11 +57,15 @@
void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer);
+ // Special caching slot for the layer caching feature.
+ static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+
private:
// an array where the index corresponds to a slot and the value corresponds to a (counter,
// buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
// or used and allows us to keep track of the least-recently used buffer.
- wp<GraphicBuffer> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+ static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
+ wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index eeb20fc..f10ff25 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -20,6 +20,7 @@
#include <compositionengine/Output.h>
#include <compositionengine/impl/ClientCompositionRequestCache.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
#include <memory>
@@ -28,21 +29,18 @@
namespace android::compositionengine::impl {
-namespace planner {
-class Planner;
-} // namespace planner
-
// The implementation class contains the common implementation, but does not
// actually contain the final output state.
class Output : public virtual compositionengine::Output {
public:
- Output();
+ Output() = default;
~Output() override;
// compositionengine::Output overrides
bool isValid() const override;
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
+ void setLayerCachingEnabled(bool) override;
void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) override;
void setDisplaySize(const ui::Size&) override;
@@ -51,6 +49,7 @@
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
void setColorProfile(const ColorProfile&) override;
+ void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) override;
void dump(std::string&) const override;
void dumpPlannerInfo(const Vector<String16>& args, std::string&) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 8f767d3..d41c2dd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -115,6 +115,15 @@
// Current target dataspace
ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+ // The earliest time to send the present command to the HAL
+ std::chrono::steady_clock::time_point earliestPresentTime;
+
+ // Current display brightness
+ float displayBrightnessNits{-1.f};
+
+ // SDR white point
+ float sdrWhitePointNits{-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 ae88e78..2ffd472 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <cstdint>
#include <memory>
#include <string>
@@ -42,7 +43,8 @@
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
- void writeStateToHWC(bool includeGeometry, bool skipLayer) override;
+ void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
+ bool isPeekingThrough) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
@@ -66,7 +68,8 @@
private:
Rect calculateInitialCrop() const;
- void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+ void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+ uint32_t z);
void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
@@ -74,9 +77,11 @@
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
- void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+ void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+ bool isPeekingThrough, bool skipLayer);
void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
Hwc2::IComposerClient::Composition to) const;
+ bool isClientCompositionForced(bool isPeekingThrough) const;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index c61ec59..3f670a1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -46,6 +46,10 @@
class HWComposer;
+namespace compositionengine {
+class OutputLayer;
+} // namespace compositionengine
+
namespace compositionengine::impl {
// Note that fields that affect HW composer state may need to be mirrored into
@@ -84,9 +88,6 @@
// The dataspace for this layer
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
- // The Z order index of this layer on this output
- uint32_t z{0};
-
// Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
struct {
std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
@@ -96,6 +97,12 @@
ProjectionSpace displaySpace;
Region damageRegion = Region::INVALID_REGION;
Region visibleRegion;
+
+ // The OutputLayer pointed to by this field will be rearranged to draw
+ // behind the OutputLayer represented by this CompositionState and will
+ // be visible through it. Unowned - the OutputLayer's lifetime will
+ // outlast this.)
+ compositionengine::OutputLayer* peekThroughLayer = nullptr;
} overrideInfo;
/*
@@ -118,6 +125,9 @@
// Set to true when overridden info has been sent to HW composer
bool stateOverridden = false;
+
+ // True when this layer was skipped as part of SF-side layer caching.
+ bool layerSkipped = false;
};
// The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 53f4a30..fdcd6ab 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -60,7 +60,6 @@
void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
- NonBufferHash getFingerprint() const { return mFingerprint; }
size_t getLayerCount() const { return mLayers.size(); }
const Layer& getFirstLayer() const { return mLayers[0]; }
const Rect& getBounds() const { return mBounds; }
@@ -70,6 +69,7 @@
const sp<Fence>& getDrawFence() const { return mDrawFence; }
const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
+ const std::vector<Layer>& getConstituentLayers() const { return mLayers; }
NonBufferHash getNonBufferHash() const;
@@ -78,6 +78,7 @@
size_t getDisplayCost() const;
bool hasBufferUpdate() const;
+ bool hasRenderedBuffer() const { return mTexture != nullptr; }
bool hasReadyBuffer() const;
// Decomposes this CachedSet into a vector of its layers as individual CachedSets
@@ -105,12 +106,35 @@
void dump(std::string& result) const;
+ // Whether this represents a single layer with a buffer and rounded corners.
+ // If it is, we may be able to draw it by placing it behind another
+ // CachedSet and punching a hole.
+ bool requiresHolePunch() const;
+
+ // True if any constituent layer is configured to blur any layers behind.
+ bool hasBlurBehind() const;
+
+ // Add a layer that will be drawn behind this one. ::render() will render a
+ // hole in this CachedSet's buffer, allowing the supplied layer to peek
+ // through. Must be called before ::render().
+ // Will do nothing if this CachedSet is not opaque where the hole punch
+ // layer is displayed.
+ // If isFirstLayer is true, this CachedSet can be considered opaque because
+ // nothing (besides the hole punch layer) will be drawn behind it.
+ void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer);
+
+ // Retrieve the layer that will be drawn behind this one.
+ compositionengine::OutputLayer* getHolePunchLayer() const;
+
private:
CachedSet() = default;
const NonBufferHash mFingerprint;
std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
std::vector<Layer> mLayers;
+
+ // Unowned.
+ const LayerState* mHolePunchLayer = nullptr;
Rect mBounds = Rect::EMPTY_RECT;
Region mVisibleRegion;
size_t mAge = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 2f2ad4c..213c55e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -20,6 +20,7 @@
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/LayerState.h>
+#include <numeric>
#include <vector>
namespace android {
@@ -36,7 +37,7 @@
class Flattener {
public:
- Flattener(Predictor& predictor) : mPredictor(predictor) {}
+ Flattener(bool enableHolePunch = false) : mEnableHolePunch(enableHolePunch) {}
void setDisplaySize(ui::Size size) { mDisplaySize = size; }
@@ -48,6 +49,7 @@
const OutputCompositionState& outputState);
void dump(std::string& result) const;
+ void dumpLayers(std::string& result) const;
private:
size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
@@ -59,9 +61,76 @@
bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
std::chrono::steady_clock::time_point now);
+ // A Run is a sequence of CachedSets, which is a candidate for flattening into a single
+ // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1
+ // CachedSet
+ class Run {
+ public:
+ // A builder for a Run, to aid in construction
+ class Builder {
+ private:
+ std::vector<CachedSet>::const_iterator mStart;
+ std::vector<size_t> mLengths;
+ const CachedSet* mHolePunchCandidate = nullptr;
+
+ 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());
+ }
+
+ // 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); }
+
+ // Sets the hole punch candidate for the Run.
+ void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
+ mHolePunchCandidate = holePunchCandidate;
+ }
+
+ // Builds a Run instance, if a valid Run may be built.
+ std::optional<Run> validateAndBuild() {
+ if (mLengths.size() <= 1) {
+ return std::nullopt;
+ }
+
+ return Run(mStart,
+ std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
+ [](size_t left, size_t right) { return left + right; }),
+ mHolePunchCandidate);
+ }
+
+ void reset() { *this = {}; }
+ };
+
+ // Gets the starting CachedSet of this run.
+ // This is an iterator into mLayers
+ const std::vector<CachedSet>::const_iterator& getStart() const { return mStart; }
+ // Gets the total number of layers encompassing this Run.
+ size_t getLayerLength() const { return mLength; }
+ // Gets the hole punch candidate for this Run.
+ const CachedSet* getHolePunchCandidate() const { return mHolePunchCandidate; }
+
+ private:
+ Run(std::vector<CachedSet>::const_iterator start, size_t length,
+ const CachedSet* holePunchCandidate)
+ : mStart(start), mLength(length), mHolePunchCandidate(holePunchCandidate) {}
+ const std::vector<CachedSet>::const_iterator mStart;
+ const size_t mLength;
+ const CachedSet* const mHolePunchCandidate;
+
+ friend class Builder;
+ };
+
+ std::vector<Run> findCandidateRuns(std::chrono::steady_clock::time_point now) const;
+
+ std::optional<Run> findBestRun(std::vector<Run>& runs) const;
+
void buildCachedSets(std::chrono::steady_clock::time_point now);
- Predictor& mPredictor;
+ const bool mEnableHolePunch;
ui::Size mDisplaySize;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index f2f6c0b..fef0dfb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -26,6 +26,7 @@
#include <string>
#include "DisplayHardware/Hal.h"
+#include "math/HashCombine.h"
namespace std {
template <typename T>
@@ -48,25 +49,26 @@
// clang-format off
enum class LayerStateField : uint32_t {
- None = 0u,
- Id = 1u << 0,
- Name = 1u << 1,
- DisplayFrame = 1u << 2,
- SourceCrop = 1u << 3,
- ZOrder = 1u << 4,
- BufferTransform = 1u << 5,
- BlendMode = 1u << 6,
- Alpha = 1u << 7,
- LayerMetadata = 1u << 8,
- VisibleRegion = 1u << 9,
- Dataspace = 1u << 10,
- PixelFormat = 1u << 11,
- ColorTransform = 1u << 12,
- SurfaceDamage = 1u << 13,
- CompositionType = 1u << 14,
- SidebandStream = 1u << 15,
- Buffer = 1u << 16,
- SolidColor = 1u << 17,
+ None = 0u,
+ Id = 1u << 0,
+ Name = 1u << 1,
+ DisplayFrame = 1u << 2,
+ SourceCrop = 1u << 3,
+ BufferTransform = 1u << 4,
+ BlendMode = 1u << 5,
+ Alpha = 1u << 6,
+ LayerMetadata = 1u << 7,
+ VisibleRegion = 1u << 8,
+ Dataspace = 1u << 9,
+ PixelFormat = 1u << 10,
+ ColorTransform = 1u << 11,
+ SurfaceDamage = 1u << 12,
+ CompositionType = 1u << 13,
+ SidebandStream = 1u << 14,
+ Buffer = 1u << 15,
+ SolidColor = 1u << 16,
+ BackgroundBlurRadius = 1u << 17,
+ BlurRegions = 1u << 18,
};
// clang-format on
@@ -226,6 +228,9 @@
const std::string& getName() const { return mName.get(); }
Rect getDisplayFrame() const { return mDisplayFrame.get(); }
const Region& getVisibleRegion() const { return mVisibleRegion.get(); }
+ bool hasBlurBehind() const {
+ return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
+ }
hardware::graphics::composer::hal::Composition getCompositionType() const {
return mCompositionType.get();
}
@@ -271,9 +276,6 @@
rect.top, rect.right, rect.bottom)};
}};
- OutputLayerState<uint32_t, LayerStateField::ZOrder> mZOrder{
- [](auto layer) { return layer->getState().z; }};
-
using BufferTransformState = OutputLayerState<hardware::graphics::composer::hal::Transform,
LayerStateField::BufferTransform>;
BufferTransformState mBufferTransform{[](auto layer) {
@@ -402,7 +404,45 @@
return std::vector<std::string>{stream.str()};
}};
- static const constexpr size_t kNumNonUniqueFields = 15;
+ OutputLayerState<int32_t, LayerStateField::BackgroundBlurRadius> mBackgroundBlurRadius{
+ [](auto layer) {
+ return layer->getLayerFE().getCompositionState()->backgroundBlurRadius;
+ }};
+
+ using BlurRegionsState =
+ OutputLayerState<std::vector<BlurRegion>, LayerStateField::BlurRegions>;
+ BlurRegionsState mBlurRegions{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->blurRegions;
+ },
+ [](const std::vector<BlurRegion>& regions) {
+ std::vector<std::string> result;
+ for (const auto region : regions) {
+ std::string str;
+ base::StringAppendF(&str,
+ "{radius=%du, cornerRadii=[%f, %f, "
+ "%f, %f], alpha=%f, rect=[%d, "
+ "%d, %d, %d]",
+ region.blurRadius,
+ region.cornerRadiusTL,
+ region.cornerRadiusTR,
+ region.cornerRadiusBL,
+ region.cornerRadiusBR, region.alpha,
+ region.left, region.top, region.right,
+ region.bottom);
+ result.push_back(str);
+ }
+ return result;
+ },
+ BlurRegionsState::getDefaultEquals(),
+ [](const std::vector<BlurRegion>& regions) {
+ size_t hash = 0;
+ for (const auto& region : regions) {
+ android::hashCombineSingle(hash, region);
+ }
+ return hash;
+ }};
+
+ static const constexpr size_t kNumNonUniqueFields = 16;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -417,10 +457,10 @@
std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
return {
- &mDisplayFrame, &mSourceCrop, &mZOrder, &mBufferTransform,
- &mBlendMode, &mAlpha, &mLayerMetadata, &mVisibleRegion,
- &mOutputDataspace, &mPixelFormat, &mColorTransform, &mCompositionType,
- &mSidebandStream, &mBuffer, &mSolidColor,
+ &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
+ &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
+ &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
+ &mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index e6d2b63..4365b93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -41,7 +41,7 @@
// as a more efficient representation of parts of the layer stack.
class Planner {
public:
- Planner() : mFlattener(mPredictor) {}
+ Planner();
void setDisplaySize(ui::Size);
@@ -76,6 +76,8 @@
std::optional<Predictor::PredictedPlan> mPredictedPlan;
NonBufferHash mFlattenedHash = 0;
+
+ bool mPredictorEnabled = false;
};
} // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index dde8999..d215bda 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -43,6 +43,7 @@
MOCK_CONST_METHOD0(getDebugName, const char*());
MOCK_CONST_METHOD0(getSequence, int32_t());
+ MOCK_CONST_METHOD0(hasRoundedCorners, bool());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 5aa53e5..4b4d375 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,6 +36,7 @@
MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
+ MOCK_METHOD1(setLayerCachingEnabled, void(bool));
MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
@@ -43,6 +44,7 @@
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
+ MOCK_METHOD2(setDisplayBrightness, void(float, float));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD2(dumpPlannerInfo, void(const Vector<String16>&, std::string&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 2454ff7..358ed5a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -22,6 +22,7 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <gmock/gmock.h>
+#include <cstdint>
namespace android::compositionengine::mock {
@@ -39,7 +40,7 @@
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
- MOCK_METHOD2(writeStateToHWC, void(bool, bool));
+ MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index 2d9f01b..7e020ee 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -36,7 +36,8 @@
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
- lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+ lhs.stretchEffect == rhs.stretchEffect;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
@@ -45,8 +46,7 @@
lhs.textureTransform == rhs.textureTransform &&
lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
- lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
- lhs.maxContentLuminance == rhs.maxContentLuminance;
+ lhs.maxLuminanceNits == rhs.maxLuminanceNits;
}
inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index a605fe1..953eb76 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -192,16 +192,7 @@
if (const auto halDisplayId = HalDisplayId::tryCast(mId);
outputLayer && !mIsDisconnected && halDisplayId) {
auto& hwc = getCompositionEngine().getHwComposer();
- // Note: For the moment we ensure it is safe to take a reference to the
- // HWComposer implementation by destroying all the OutputLayers (and
- // hence the HWC2::Layers they own) before setting a new HWComposer. See
- // for example SurfaceFlinger::updateVrFlinger().
- // TODO(b/121291683): Make this safer.
- auto hwcLayer =
- std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId),
- [&hwc, id = *halDisplayId](HWC2::Layer* layer) {
- hwc.destroyLayer(id, layer);
- });
+ auto hwcLayer = hwc.createLayer(*halDisplayId);
ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
getName().c_str());
outputLayer->setHwcLayer(std::move(hwcLayer));
@@ -267,7 +258,7 @@
auto& hwc = getCompositionEngine().getHwComposer();
if (status_t result =
hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
- &changes);
+ getState().earliestPresentTime, &changes);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
@@ -368,7 +359,7 @@
}
auto& hwc = getCompositionEngine().getHwComposer();
- hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
+ hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 0cc2c6e..5565396 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -101,9 +101,9 @@
}
void dumpVal(std::string& out, const char* name, const StretchEffect& effect) {
- StringAppendF(&out, "%s={ area=[%f, %f, %f, %f], vec=(%f, %f), max=%f } ", name,
- effect.area.left, effect.area.top, effect.area.right, effect.area.bottom,
- effect.vectorX, effect.vectorY, effect.maxAmount);
+ StringAppendF(&out, "%s={ width =%f, height = %f, vec=(%f, %f), max=(%f, %f) } ", name,
+ effect.width, effect.height,
+ effect.vectorX, effect.vectorY, effect.maxAmountX, effect.maxAmountY);
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index cedc333..f95382d 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -36,7 +36,7 @@
sp<GraphicBuffer>* outBuffer) {
// default is 0
if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
- slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+ slot >= static_cast<int32_t>(kMaxLayerBufferCount)) {
*outSlot = 0;
} else {
*outSlot = static_cast<uint32_t>(slot);
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 430945a..ff7d430 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -78,6 +78,20 @@
dumpVal(out, "stretchEffect", stretchEffect);
}
+ if (!blurRegions.empty()) {
+ out.append("\n blurRegions {");
+ for (const auto& region : blurRegions) {
+ out.append("\n ");
+ base::StringAppendF(&out,
+ "{radius=%du, cornerRadii=[%f, %f, %f, %f], alpha=%f, rect=[%d, "
+ "%d, %d, %d]",
+ region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR,
+ region.cornerRadiusBL, region.cornerRadiusBR, region.alpha,
+ region.left, region.top, region.right, region.bottom);
+ }
+ out.append("\n }\n ");
+ }
+
if (!metadata.empty()) {
out.append("\n metadata {");
for (const auto& [key, entry] : metadata) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3468b20..088a400 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -55,18 +55,6 @@
namespace impl {
-Output::Output() {
- const bool enableLayerCaching = [] {
- const bool enable =
- android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
- return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
- }();
-
- if (enableLayerCaching) {
- mPlanner = std::make_unique<planner::Planner>();
- }
-}
-
namespace {
template <typename T>
@@ -135,6 +123,29 @@
dirtyEntireOutput();
}
+void Output::setLayerCachingEnabled(bool enabled) {
+ if (enabled == (mPlanner != nullptr)) {
+ return;
+ }
+
+ if (enabled) {
+ mPlanner = std::make_unique<planner::Planner>();
+ if (mRenderSurface) {
+ mPlanner->setDisplaySize(mRenderSurface->getSize());
+ }
+ } else {
+ mPlanner.reset();
+ }
+
+ for (auto* outputLayer : getOutputLayersOrderedByZ()) {
+ if (!outputLayer) {
+ continue;
+ }
+
+ outputLayer->editState().overrideInfo = {};
+ }
+}
+
void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) {
auto& outputState = editState();
@@ -256,6 +267,18 @@
dirtyEntireOutput();
}
+void Output::setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) {
+ auto& outputState = editState();
+ if (outputState.sdrWhitePointNits == sdrWhitePointNits &&
+ outputState.displayBrightnessNits == displayBrightnessNits) {
+ // Nothing changed
+ return;
+ }
+ outputState.sdrWhitePointNits = sdrWhitePointNits;
+ outputState.displayBrightnessNits = displayBrightnessNits;
+ dirtyEntireOutput();
+}
+
void Output::dump(std::string& out) const {
using android::base::StringAppendF;
@@ -455,12 +478,6 @@
setReleasedLayers(refreshArgs);
finalizePendingOutputLayers();
-
- // Generate a simple Z-order values to each visible output layer
- uint32_t zOrder = 0;
- for (auto* outputLayer : getOutputLayersOrderedByZ()) {
- outputLayer->editState().z = zOrder++;
- }
}
void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
@@ -711,20 +728,47 @@
return;
}
+ editState().earliestPresentTime = refreshArgs.earliestPresentTime;
+
+ compositionengine::OutputLayer* peekThroughLayer = nullptr;
sp<GraphicBuffer> previousOverride = nullptr;
+ bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
+ uint32_t z = 0;
+ bool overrideZ = false;
for (auto* layer : getOutputLayersOrderedByZ()) {
+ if (layer == peekThroughLayer) {
+ // No longer needed, although it should not show up again, so
+ // resetting it is not truly needed either.
+ peekThroughLayer = nullptr;
+
+ // peekThroughLayer was already drawn ahead of its z order.
+ continue;
+ }
bool skipLayer = false;
- if (layer->getState().overrideInfo.buffer != nullptr) {
- if (previousOverride != nullptr &&
- layer->getState().overrideInfo.buffer->getBuffer() == previousOverride) {
+ const auto& overrideInfo = layer->getState().overrideInfo;
+ if (overrideInfo.buffer != nullptr) {
+ if (previousOverride && overrideInfo.buffer->getBuffer() == previousOverride) {
ALOGV("Skipping redundant buffer");
skipLayer = true;
+ } else {
+ // First layer with the override buffer.
+ if (overrideInfo.peekThroughLayer) {
+ peekThroughLayer = overrideInfo.peekThroughLayer;
+
+ // Draw peekThroughLayer first.
+ overrideZ = true;
+ includeGeometry = true;
+ constexpr bool isPeekingThrough = true;
+ peekThroughLayer->writeStateToHWC(includeGeometry, false, z++, overrideZ,
+ isPeekingThrough);
+ }
+
+ previousOverride = overrideInfo.buffer->getBuffer();
}
- previousOverride = layer->getState().overrideInfo.buffer->getBuffer();
}
- const bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
- layer->writeStateToHWC(includeGeometry, skipLayer);
+ constexpr bool isPeekingThrough = false;
+ layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough);
}
}
@@ -1011,8 +1055,13 @@
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
? outputState.dataspace
: ui::Dataspace::UNKNOWN;
- clientCompositionDisplay.maxLuminance =
- mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+
+ // If we have a valid current display brightness use that, otherwise fall back to the
+ // display's max desired
+ clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
+ ? outputState.displayBrightnessNits
+ : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+ clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits;
// Compute the global color transform matrix.
if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 9ca8914..cd14327 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -14,13 +14,16 @@
* limitations under the License.
*/
+#include <DisplayHardware/Hal.h>
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
+#include <compositionengine/impl/HwcBufferCache.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <cstdint>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -312,7 +315,8 @@
}
}
-void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer) {
+void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
+ bool zIsOverridden, bool isPeekingThrough) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -335,10 +339,11 @@
// TODO(b/181172795): We now update geometry for all flattened layers. We should update it
// only when the geometry actually changes
- const bool isOverridden = state.overrideInfo.buffer != nullptr;
+ const bool isOverridden =
+ state.overrideInfo.buffer != nullptr || isPeekingThrough || zIsOverridden;
const bool prevOverridden = state.hwc->stateOverridden;
if (isOverridden || prevOverridden || skipLayer || includeGeometry) {
- writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
+ writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType, z);
writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
skipLayer);
}
@@ -346,16 +351,19 @@
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
- writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
+ writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
+ skipLayer);
// Always set the layer color after setting the composition type.
writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
editState().hwc->stateOverridden = isOverridden;
+ editState().hwc->layerSkipped = skipLayer;
}
-void OutputLayer::writeOutputDependentGeometryStateToHWC(
- HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
+void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
+ hal::Composition requestedCompositionType,
+ uint32_t z) {
const auto& outputDependentState = getState();
Rect displayFrame = outputDependentState.displayFrame;
@@ -363,7 +371,12 @@
if (outputDependentState.overrideInfo.buffer != nullptr) {
displayFrame = outputDependentState.overrideInfo.displayFrame;
- sourceCrop = displayFrame.toFloatRect();
+ sourceCrop =
+ FloatRect(0.f, 0.f,
+ static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+ ->getWidth()),
+ static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+ ->getHeight()));
}
ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
@@ -382,9 +395,9 @@
sourceCrop.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(),
- outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
+ if (auto error = hwcLayer->setZOrder(z); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(), z,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
// Solid-color layers and overridden buffers should always use an identity transform.
@@ -403,7 +416,10 @@
void OutputLayer::writeOutputIndependentGeometryStateToHWC(
HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
bool skipLayer) {
- const auto blendMode = getState().overrideInfo.buffer
+ // If there is a peekThroughLayer, then this layer has a hole in it. We need to use
+ // PREMULTIPLIED so it will peek through.
+ const auto& overrideInfo = getState().overrideInfo;
+ const auto blendMode = overrideInfo.buffer || overrideInfo.peekThroughLayer
? hardware::graphics::composer::hal::BlendMode::PREMULTIPLIED
: outputIndependentState.blendMode;
if (auto error = hwcLayer->setBlendMode(blendMode); error != hal::Error::NONE) {
@@ -469,7 +485,8 @@
const Region& surfaceDamage = getState().overrideInfo.buffer
? getState().overrideInfo.damageRegion
- : outputIndependentState.surfaceDamage;
+ : (getState().hwc->stateOverridden ? Region::INVALID_REGION
+ : outputIndependentState.surfaceDamage);
if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
@@ -536,9 +553,11 @@
sp<GraphicBuffer> buffer = outputIndependentState.buffer;
sp<Fence> acquireFence = outputIndependentState.acquireFence;
+ int slot = outputIndependentState.bufferSlot;
if (getState().overrideInfo.buffer != nullptr) {
buffer = getState().overrideInfo.buffer->getBuffer();
acquireFence = getState().overrideInfo.acquireFence;
+ slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
}
ALOGV("Writing buffer %p", buffer.get());
@@ -547,8 +566,7 @@
sp<GraphicBuffer> hwcBuffer;
// We need access to the output-dependent state for the buffer cache there,
// though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer,
- &hwcSlot, &hwcBuffer);
+ editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
error != hal::Error::NONE) {
@@ -558,16 +576,20 @@
}
void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
- hal::Composition requestedCompositionType) {
+ hal::Composition requestedCompositionType,
+ bool isPeekingThrough, bool skipLayer) {
auto& outputDependentState = editState();
- // If we are forcing client composition, we need to tell the HWC
- if (outputDependentState.forceClientComposition) {
+ if (isClientCompositionForced(isPeekingThrough)) {
+ // If we are forcing client composition, we need to tell the HWC
requestedCompositionType = hal::Composition::CLIENT;
}
// Set the requested composition type with the HWC whenever it changes
- if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
+ // We also resend the composition type when this layer was previously skipped, to ensure that
+ // the composition type is up-to-date.
+ if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType ||
+ (outputDependentState.hwc->layerSkipped && !skipLayer)) {
outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
@@ -647,12 +669,22 @@
}
}
+bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const {
+ return getState().forceClientComposition ||
+ (!isPeekingThrough && getLayerFE().hasRoundedCorners());
+}
+
void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
auto& state = editState();
LOG_FATAL_IF(!state.hwc);
auto& hwcState = *state.hwc;
- detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+ // Only detected disallowed changes if this was not a skip layer, because the
+ // validated composition type may be arbitrary (usually DEVICE, to reflect that there were
+ // fewer GPU layers)
+ if (!hwcState.layerSkipped) {
+ detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+ }
hwcState.hwcCompositionType = compositionType;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index efd23dc..b4c314c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -67,7 +67,6 @@
dumpVal(out, "sourceCrop", sourceCrop);
dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
dumpVal(out, "dataspace", toString(dataspace), dataspace);
- dumpVal(out, "z-index", z);
dumpVal(out, "override buffer", overrideInfo.buffer.get());
dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
dumpVal(out, "override display frame", overrideInfo.displayFrame);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 9955e29..b61daeb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "Planner"
// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/properties.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -25,6 +26,8 @@
#include <renderengine/DisplaySettings.h>
#include <renderengine/RenderEngine.h>
+#include <utils/Trace.h>
+
namespace android::compositionengine::impl::planner {
const bool CachedSet::sDebugHighlighLayers =
@@ -154,12 +157,17 @@
void CachedSet::render(renderengine::RenderEngine& renderEngine,
const OutputCompositionState& outputState) {
+ ATRACE_CALL();
const Rect& viewport = outputState.layerStackSpace.content;
const ui::Dataspace& outputDataspace = outputState.dataspace;
const ui::Transform::RotationFlags orientation =
ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+
renderengine::DisplaySettings displaySettings{
- .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
+ .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left,
+ -mBounds.top + outputState.framebufferSpace.content.top,
+ -mBounds.left + outputState.framebufferSpace.content.right,
+ -mBounds.top + outputState.framebufferSpace.content.bottom),
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
@@ -193,6 +201,23 @@
std::transform(layerSettings.cbegin(), layerSettings.cend(),
std::back_inserter(layerSettingsPointers),
[](const renderengine::LayerSettings& settings) { return &settings; });
+ renderengine::LayerSettings holePunchSettings;
+ if (mHolePunchLayer) {
+ auto clientCompositionList =
+ mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ targetSettings);
+ // Assume that the final layer contains the buffer that we want to
+ // replace with a hole punch.
+ holePunchSettings = clientCompositionList.back();
+ LOG_ALWAYS_FATAL_IF(!holePunchSettings.source.buffer.buffer, "Expected to have a buffer!");
+ // This mimics Layer::prepareClearClientComposition
+ holePunchSettings.source.buffer.buffer = nullptr;
+ holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+ holePunchSettings.disableBlending = true;
+ holePunchSettings.alpha = 0.0f;
+ holePunchSettings.name = std::string("hole punch layer");
+ layerSettingsPointers.push_back(&holePunchSettings);
+ }
if (sDebugHighlighLayers) {
highlight = {
@@ -229,9 +254,7 @@
if (result == NO_ERROR) {
mDrawFence = new Fence(drawFence.release());
- mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(),
- outputState.framebufferSpace.bounds.getHeight()),
- mBounds);
+ mOutputSpace = outputState.framebufferSpace;
mTexture = std::move(texture);
mOutputSpace.orientation = outputState.framebufferSpace.orientation;
mOutputDataspace = outputDataspace;
@@ -241,6 +264,61 @@
}
}
+bool CachedSet::requiresHolePunch() const {
+ // In order for the hole punch to be beneficial, the layer must be updating
+ // regularly, meaning it should not have been merged with other layers.
+ if (getLayerCount() != 1) {
+ return false;
+ }
+
+ // There is no benefit to a hole punch unless the layer has a buffer.
+ if (!mLayers[0].getBuffer()) {
+ return false;
+ }
+
+ const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
+ if (layerFE.getCompositionState()->forceClientComposition) {
+ return false;
+ }
+
+ return layerFE.hasRoundedCorners();
+}
+
+bool CachedSet::hasBlurBehind() const {
+ return std::any_of(mLayers.cbegin(), mLayers.cend(),
+ [](const Layer& layer) { return layer.getState()->hasBlurBehind(); });
+}
+
+namespace {
+bool contains(const Rect& outer, const Rect& inner) {
+ return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
+ outer.bottom >= inner.bottom;
+}
+}; // namespace
+
+void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) {
+ // Verify that this CachedSet is opaque where the hole punch layer
+ // will draw.
+ const Rect& holePunchBounds = holePunchLayer.getBounds();
+ for (const auto& layer : mLayers) {
+ // The first layer is considered opaque because nothing is behind it.
+ // Note that isOpaque is always false for a layer with rounded
+ // corners, even if the interior is opaque. In theory, such a layer
+ // could be used for a hole punch, but this is unlikely to happen in
+ // practice.
+ const auto* outputLayer = layer.getState()->getOutputLayer();
+ if (contains(outputLayer->getState().displayFrame, holePunchBounds) &&
+ (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) {
+ mHolePunchLayer = holePunchLayer.getFirstLayer().getState();
+ return;
+ }
+ }
+}
+
+compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
+ return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
+}
+
void CachedSet::dump(std::string& result) const {
const auto now = std::chrono::steady_clock::now();
@@ -248,6 +326,11 @@
std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
mFingerprint, durationString(lastUpdate).c_str(), mAge);
+ {
+ const auto b = mTexture ? mTexture->getBuffer().get() : nullptr;
+ base::StringAppendF(&result, " Override buffer: %p\n", b);
+ }
+ base::StringAppendF(&result, " HolePunchLayer: %p\n", mHolePunchLayer);
if (mLayers.size() == 1) {
base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 9c9649c..2def99d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -17,22 +17,60 @@
#undef LOG_TAG
#define LOG_TAG "Planner"
// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
+
+#include <utils/Trace.h>
using time_point = std::chrono::steady_clock::time_point;
using namespace std::chrono_literals;
namespace android::compositionengine::impl::planner {
+namespace {
+
+// True if the underlying layer stack is the same modulo state that would be expected to be
+// different like specific buffers, false otherwise.
+bool isSameStack(const std::vector<const LayerState*>& incomingLayers,
+ const std::vector<CachedSet>& cachedSets) {
+ std::vector<const LayerState*> existingLayers;
+ for (auto& cachedSet : cachedSets) {
+ for (auto& layer : cachedSet.getConstituentLayers()) {
+ existingLayers.push_back(layer.getState());
+ }
+ }
+
+ if (incomingLayers.size() != existingLayers.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < incomingLayers.size(); i++) {
+ // Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try
+ // to access destroyed OutputLayers later on.
+ if (incomingLayers[i]->getId() != existingLayers[i]->getId() ||
+ incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
NonBufferHash hash, time_point now) {
+ ATRACE_CALL();
const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
mUnflattenedDisplayCost += unflattenedDisplayCost;
- if (mCurrentGeometry != hash) {
+ // We invalidate the layer cache if:
+ // 1. We're not tracking any layers, or
+ // 2. The last seen hashed geometry changed between frames, or
+ // 3. A stricter equality check demonstrates that the layer stack really did change, since the
+ // hashed geometry does not guarantee uniqueness.
+ if (mCurrentGeometry != hash || (!mLayers.empty() && !isSameStack(layers, mLayers))) {
resetActivities(hash, now);
mFlattenedDisplayCost += unflattenedDisplayCost;
return hash;
@@ -57,13 +95,22 @@
void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
const OutputCompositionState& outputState) {
- if (!mNewCachedSet) {
+ ATRACE_CALL();
+ if (!mNewCachedSet || mNewCachedSet->hasRenderedBuffer()) {
return;
}
mNewCachedSet->render(renderEngine, outputState);
}
+void Flattener::dumpLayers(std::string& result) const {
+ result.append(" Current layers:");
+ for (const CachedSet& layer : mLayers) {
+ result.append("\n");
+ layer.dump(result);
+ }
+}
+
void Flattener::dump(std::string& result) const {
const auto now = std::chrono::steady_clock::now();
@@ -83,9 +130,11 @@
return left.first < right.first;
};
- const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
- mInitialLayerCounts.cend(), compareLayerCounts)
- ->first;
+ const size_t maxLayerCount = mInitialLayerCounts.empty()
+ ? 0u
+ : std::max_element(mInitialLayerCounts.cbegin(), mInitialLayerCounts.cend(),
+ compareLayerCounts)
+ ->first;
result.append("\n Initial counts:\n");
for (size_t count = 1; count < maxLayerCount; ++count) {
@@ -108,11 +157,7 @@
base::StringAppendF(&result, "\n Current hash %016zx, last update %sago\n\n", mCurrentGeometry,
durationString(lastUpdate).c_str());
- result.append(" Current layers:");
- for (const CachedSet& layer : mLayers) {
- result.append("\n");
- layer.dump(result);
- }
+ dumpLayers(result);
}
size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
@@ -173,6 +218,7 @@
// was already populated with these layers, i.e. on the second and following
// calls with the same geometry.
bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) {
+ ATRACE_CALL();
std::vector<CachedSet> merged;
if (mLayers.empty()) {
@@ -200,7 +246,8 @@
auto currentLayerIter = mLayers.begin();
auto incomingLayerIter = layers.begin();
while (incomingLayerIter != layers.end()) {
- if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) {
+ if (mNewCachedSet &&
+ mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
if (mNewCachedSet->hasBufferUpdate()) {
ALOGV("[%s] Dropping new cached set", __func__);
++mInvalidatedCachedSetAges[0];
@@ -209,6 +256,7 @@
ALOGV("[%s] Found ready buffer", __func__);
size_t skipCount = mNewCachedSet->getLayerCount();
while (skipCount != 0) {
+ auto* peekThroughLayer = mNewCachedSet->getHolePunchLayer();
const size_t layerCount = currentLayerIter->getLayerCount();
for (size_t i = 0; i < layerCount; ++i) {
OutputLayer::CompositionState& state =
@@ -221,6 +269,7 @@
.displaySpace = mNewCachedSet->getOutputSpace(),
.damageRegion = Region::INVALID_REGION,
.visibleRegion = mNewCachedSet->getVisibleRegion(),
+ .peekThroughLayer = peekThroughLayer,
};
++incomingLayerIter;
}
@@ -244,6 +293,7 @@
// Skip the incoming layers corresponding to this valid current layer
const size_t layerCount = currentLayerIter->getLayerCount();
+ auto* peekThroughLayer = currentLayerIter->getHolePunchLayer();
for (size_t i = 0; i < layerCount; ++i) {
OutputLayer::CompositionState& state =
(*incomingLayerIter)->getOutputLayer()->editState();
@@ -255,6 +305,7 @@
.displaySpace = currentLayerIter->getOutputSpace(),
.damageRegion = Region(),
.visibleRegion = currentLayerIter->getVisibleRegion(),
+ .peekThroughLayer = peekThroughLayer,
};
++incomingLayerIter;
}
@@ -282,67 +333,105 @@
return true;
}
-void Flattener::buildCachedSets(time_point now) {
- struct Run {
- Run(std::vector<CachedSet>::const_iterator start, size_t length)
- : start(start), length(length) {}
-
- std::vector<CachedSet>::const_iterator start;
- size_t length;
- };
-
- if (mLayers.empty()) {
- ALOGV("[%s] No layers found, returning", __func__);
- return;
- }
-
+std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const {
+ ATRACE_CALL();
std::vector<Run> runs;
bool isPartOfRun = false;
+ Run::Builder builder;
+ bool firstLayer = true;
+ bool runHasFirstLayer = false;
+
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
- if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
- // Layer is inactive
+ const bool layerIsInactive = now - currentSet->getLastUpdate() > kActiveLayerTimeout;
+ const bool layerHasBlur = currentSet->hasBlurBehind();
+ if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur)) {
if (isPartOfRun) {
- runs.back().length += currentSet->getLayerCount();
+ builder.append(currentSet->getLayerCount());
} else {
// Runs can't start with a non-buffer layer
if (currentSet->getFirstLayer().getBuffer() == nullptr) {
ALOGV("[%s] Skipping initial non-buffer layer", __func__);
} else {
- runs.emplace_back(currentSet, currentSet->getLayerCount());
+ builder.init(currentSet);
+ if (firstLayer) {
+ runHasFirstLayer = true;
+ }
isPartOfRun = true;
}
}
- } else {
- // Runs must be at least 2 sets long or there's nothing to combine
- if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
- runs.pop_back();
+ } else if (isPartOfRun) {
+ builder.setHolePunchCandidate(&(*currentSet));
+ if (auto run = builder.validateAndBuild(); run) {
+ runs.push_back(*run);
}
+ runHasFirstLayer = false;
+ builder.reset();
isPartOfRun = false;
}
+
+ firstLayer = false;
}
- // Check for at least 2 sets one more time in case the set includes the last layer
- if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
- runs.pop_back();
+ // If we're in the middle of a run at the end, we still need to validate and build it.
+ if (isPartOfRun) {
+ if (auto run = builder.validateAndBuild(); run) {
+ runs.push_back(*run);
+ }
}
ALOGV("[%s] Found %zu candidate runs", __func__, runs.size());
+ return runs;
+}
+
+std::optional<Flattener::Run> Flattener::findBestRun(std::vector<Flattener::Run>& runs) const {
if (runs.empty()) {
+ return std::nullopt;
+ }
+
+ // TODO (b/181192467): Choose the best run, instead of just the first.
+ return runs[0];
+}
+
+void Flattener::buildCachedSets(time_point now) {
+ ATRACE_CALL();
+ if (mLayers.empty()) {
+ ALOGV("[%s] No layers found, returning", __func__);
return;
}
- mNewCachedSet.emplace(*runs[0].start);
+ // Don't try to build a new cached set if we already have a new one in progress
+ if (mNewCachedSet) {
+ return;
+ }
+
+ std::vector<Run> runs = findCandidateRuns(now);
+
+ std::optional<Run> bestRun = findBestRun(runs);
+
+ if (!bestRun) {
+ return;
+ }
+
+ mNewCachedSet.emplace(*bestRun->getStart());
mNewCachedSet->setLastUpdate(now);
- auto currentSet = runs[0].start;
- while (mNewCachedSet->getLayerCount() < runs[0].length) {
+ auto currentSet = bestRun->getStart();
+ while (mNewCachedSet->getLayerCount() < bestRun->getLayerLength()) {
++currentSet;
mNewCachedSet->append(*currentSet);
}
+ if (mEnableHolePunch && bestRun->getHolePunchCandidate() &&
+ bestRun->getHolePunchCandidate()->requiresHolePunch()) {
+ // Add the pip layer to mNewCachedSet, but in a special way - it should
+ // replace the buffer with a clear round rect.
+ mNewCachedSet->addHolePunchLayerIfFeasible(*bestRun->getHolePunchCandidate(),
+ bestRun->getStart() == mLayers.cbegin());
+ }
+
// TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
- mPredictor.getPredictedPlan({}, 0);
+ // and feedback into the predictor
++mCachedSetCreationCount;
mCachedSetCreationCost += mNewCachedSet->getCreationCost();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index ab85997..8423a12 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -155,10 +155,9 @@
bool operator==(const LayerState& lhs, const LayerState& rhs) {
return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame &&
- lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
- lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
- lhs.mAlpha == rhs.mAlpha && lhs.mLayerMetadata == rhs.mLayerMetadata &&
- lhs.mVisibleRegion == rhs.mVisibleRegion &&
+ lhs.mSourceCrop == rhs.mSourceCrop && lhs.mBufferTransform == rhs.mBufferTransform &&
+ lhs.mBlendMode == rhs.mBlendMode && lhs.mAlpha == rhs.mAlpha &&
+ lhs.mLayerMetadata == rhs.mLayerMetadata && lhs.mVisibleRegion == rhs.mVisibleRegion &&
lhs.mOutputDataspace == rhs.mOutputDataspace && lhs.mPixelFormat == rhs.mPixelFormat &&
lhs.mColorTransform == rhs.mColorTransform &&
lhs.mCompositionType == rhs.mCompositionType &&
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 3a2534b..2f2cc06 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -18,19 +18,33 @@
#undef LOG_TAG
#define LOG_TAG "Planner"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/properties.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
+#include <utils/Trace.h>
+
namespace android::compositionengine::impl::planner {
+Planner::Planner()
+ : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {
+ // Implicitly, layer caching must also be enabled.
+ // E.g., setprop debug.sf.enable_layer_caching 1, or
+ // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
+ mPredictorEnabled =
+ base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
+}
+
void Planner::setDisplaySize(ui::Size size) {
mFlattener.setDisplaySize(size);
}
void Planner::plan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ ATRACE_CALL();
std::unordered_set<LayerId> removedLayers;
removedLayers.reserve(mPreviousLayers.size());
@@ -94,19 +108,26 @@
const bool layersWereFlattened = hash != mFlattenedHash;
ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
- mPredictedPlan =
- mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
- : mCurrentLayers,
- mFlattenedHash);
- if (mPredictedPlan) {
- ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
- } else {
- ALOGV("[%s] No prediction found\n", __func__);
+ if (mPredictorEnabled) {
+ mPredictedPlan =
+ mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+ : mCurrentLayers,
+ mFlattenedHash);
+ if (mPredictedPlan) {
+ ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
+ } else {
+ ALOGV("[%s] No prediction found\n", __func__);
+ }
}
}
void Planner::reportFinalPlan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ ATRACE_CALL();
+ if (!mPredictorEnabled) {
+ return;
+ }
+
Plan finalPlan;
const GraphicBuffer* currentOverrideBuffer = nullptr;
bool hasSkippedLayers = false;
@@ -140,6 +161,7 @@
void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
const OutputCompositionState& outputState) {
+ ATRACE_CALL();
mFlattener.renderCachedSets(renderEngine, outputState);
}
@@ -180,7 +202,9 @@
return;
}
- mPredictor.compareLayerStacks(leftHash, rightHash, result);
+ if (mPredictorEnabled) {
+ mPredictor.compareLayerStacks(leftHash, rightHash, result);
+ }
} else if (command == "--describe" || command == "-d") {
if (args.size() < 3) {
base::StringAppendF(&result,
@@ -204,7 +228,9 @@
return;
}
- mPredictor.describeLayerStack(hash, result);
+ if (mPredictorEnabled) {
+ mPredictor.describeLayerStack(hash, result);
+ }
} else if (command == "--help" || command == "-h") {
dumpUsage(result);
} else if (command == "--similar" || command == "-s") {
@@ -227,7 +253,11 @@
return;
}
- mPredictor.listSimilarStacks(*plan, result);
+ if (mPredictorEnabled) {
+ mPredictor.listSimilarStacks(*plan, result);
+ }
+ } else if (command == "--layers" || command == "-l") {
+ mFlattener.dumpLayers(result);
} else {
base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
dumpUsage(result);
@@ -240,7 +270,9 @@
mFlattener.dump(result);
result.append("\n");
- mPredictor.dump(result);
+ if (mPredictorEnabled) {
+ mPredictor.dump(result);
+ }
}
void Planner::dumpUsage(std::string& result) const {
@@ -263,6 +295,9 @@
result.append("[--similar|-s] <plan>\n");
result.append(" Prints the example layer names for similar stacks matching <plan>\n");
+
+ result.append("[--layers|-l]\n");
+ result.append(" Prints the current layers\n");
}
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 8a83639..e12cb57 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -533,16 +533,15 @@
TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) {
sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
- StrictMock<HWC2::mock::Layer> hwcLayer;
+ auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>();
EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID)))
- .WillOnce(Return(&hwcLayer));
+ .WillOnce(Return(hwcLayer));
auto outputLayer = mDisplay->createOutputLayer(layerFE);
- EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
+ EXPECT_EQ(hwcLayer.get(), outputLayer->getHwcLayer());
- EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer));
outputLayer.reset();
}
@@ -627,7 +626,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();
@@ -649,7 +648,8 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
+ EXPECT_CALL(mHwComposer,
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
@@ -679,8 +679,9 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
- .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
+ EXPECT_CALL(mHwComposer,
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(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);
@@ -867,7 +868,8 @@
sp<Fence> layer1Fence = new Fence();
sp<Fence> layer2Fence = new Fence();
- EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _))
+ .Times(1);
EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
.WillOnce(Return(presentFence));
EXPECT_CALL(mHwComposer,
@@ -1039,7 +1041,7 @@
mDisplay->editState().isEnabled = true;
- EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
mDisplay->postFramebuffer();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index bac894a..cd2d09e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -48,15 +48,15 @@
MOCK_METHOD3(allocateVirtualDisplay,
std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
- MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
- MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
- MOCK_METHOD3(getDeviceCompositionChanges,
- status_t(HalDisplayId, bool,
+ MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
+ MOCK_METHOD4(getDeviceCompositionChanges,
+ status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD5(setClientTarget,
status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
ui::Dataspace));
- MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+ MOCK_METHOD2(presentAndGetReleaseFences,
+ status_t(HalDisplayId, std::chrono::steady_clock::time_point));
MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 4c3f494..5bd1216 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <compositionengine/impl/HwcBufferCache.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/CompositionEngine.h>
@@ -23,10 +24,11 @@
#include <gtest/gtest.h>
#include <log/log.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/PixelFormat.h>
#include "MockHWC2.h"
#include "MockHWComposer.h"
#include "RegionMatcher.h"
-#include "renderengine/mock/RenderEngine.h"
namespace android::compositionengine {
namespace {
@@ -689,7 +691,6 @@
struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
static constexpr hal::Error kError = hal::Error::UNSUPPORTED;
static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
- static constexpr uint32_t kZOrder = 21u;
static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
static constexpr Hwc2::Transform kOverrideBufferTransform = static_cast<Hwc2::Transform>(0);
static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
@@ -702,12 +703,14 @@
static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
+ static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
static const half4 kColor;
static const Rect kDisplayFrame;
static const Rect kOverrideDisplayFrame;
+ static const FloatRect kOverrideSourceCrop;
static const Region kOutputSpaceVisibleRegion;
static const Region kOverrideVisibleRegion;
static const mat4 kColorTransform;
@@ -716,7 +719,7 @@
static const HdrMetadata kHdrMetadata;
static native_handle_t* kSidebandStreamHandle;
static const sp<GraphicBuffer> kBuffer;
- std::shared_ptr<renderengine::ExternalTexture> kOverrideBuffer;
+ static const sp<GraphicBuffer> kOverrideBuffer;
static const sp<Fence> kFence;
static const sp<Fence> kOverrideFence;
static const std::string kLayerGenericMetadata1Key;
@@ -725,17 +728,11 @@
static const std::vector<uint8_t> kLayerGenericMetadata2Value;
OutputLayerWriteStateToHWCTest() {
- kOverrideBuffer = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
- renderengine::ExternalTexture::Usage::READABLE |
- renderengine::ExternalTexture::Usage::
- WRITEABLE);
auto& outputLayerState = mOutputLayer.editState();
outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
outputLayerState.displayFrame = kDisplayFrame;
outputLayerState.sourceCrop = kSourceCrop;
- outputLayerState.z = kZOrder;
outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
outputLayerState.dataspace = kDataspace;
@@ -770,7 +767,11 @@
void includeOverrideInfo() {
auto& overrideInfo = mOutputLayer.editState().overrideInfo;
- overrideInfo.buffer = kOverrideBuffer;
+ overrideInfo.buffer = std::make_shared<
+ renderengine::ExternalTexture>(kOverrideBuffer, mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::
+ WRITEABLE);
overrideInfo.acquireFence = kOverrideFence;
overrideInfo.displayFrame = kOverrideDisplayFrame;
overrideInfo.dataspace = kOverrideDataspace;
@@ -785,7 +786,7 @@
float alpha = kAlpha) {
EXPECT_CALL(*mHwcLayer, setDisplayFrame(displayFrame)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setSourceCrop(sourceCrop)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setZOrder(_)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setTransform(bufferTransform)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setBlendMode(blendMode)).WillOnce(Return(kError));
@@ -825,10 +826,11 @@
EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
}
- void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+ void expectSetHdrMetadataAndBufferCalls(uint32_t hwcSlot = kExpectedHwcSlot,
+ sp<GraphicBuffer> buffer = kBuffer,
sp<Fence> fence = kFence) {
EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
- EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
+ EXPECT_CALL(*mHwcLayer, setBuffer(hwcSlot, buffer, fence));
}
void expectGenericLayerMetadataCalls() {
@@ -852,6 +854,7 @@
84.f / 255.f};
const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
+const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{0.f, 0.f, 4.f, 5.f};
const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
Rect{1005, 1006, 1007, 1008}};
const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
@@ -865,6 +868,10 @@
native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
reinterpret_cast<native_handle_t*>(1031);
const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer =
+ new GraphicBuffer(4, 5, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
@@ -878,19 +885,22 @@
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -898,8 +908,10 @@
expectPerFrameCommonCalls();
expectNoSetCompositionTypeCall();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -921,6 +933,7 @@
mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
// Setting the composition type should happen before setting the color. We
// check this in this test only by setting up an testing::InSeqeuence
@@ -929,7 +942,8 @@
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
expectSetColorCall();
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -939,7 +953,10 @@
expectSetSidebandHandleCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -949,7 +966,10 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -959,7 +979,10 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -972,7 +995,10 @@
expectSetColorCall();
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -982,7 +1008,8 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -994,7 +1021,8 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -1007,7 +1035,10 @@
expectGenericLayerMetadataCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -1018,21 +1049,137 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
includeOverrideInfo();
- expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideDisplayFrame.toFloatRect(),
- kOverrideBufferTransform, kOverrideBlendMode, kOverrideAlpha);
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+ kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
kOverrideSurfaceDamage);
- expectSetHdrMetadataAndBufferCalls(kOverrideBuffer->getBuffer(), kOverrideFence);
+ expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->stateOverridden = true;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+ Region::INVALID_REGION);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->stateOverridden = true;
+ mOutputLayer.editState().hwc->layerSkipped = true;
+ mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+ Region::INVALID_REGION);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().forceClientComposition = true;
+ mOutputLayer.editState().hwc->stateOverridden = true;
+ mOutputLayer.editState().hwc->layerSkipped = true;
+ mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+ Region::INVALID_REGION);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
+ auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make();
+ OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE};
+
+ mOutputLayer.mState.overrideInfo.peekThroughLayer = &peekThroughLayer;
+
+ expectGeometryCommonCalls(kDisplayFrame, kSourceCrop, kBufferTransform,
+ Hwc2::IComposerClient::BlendMode::PREMULTIPLIED);
+ expectPerFrameCommonCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ true);
+ EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, zIsOverriddenSetsOverride) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/
+ false);
+ EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersForceClientComposition) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/
+ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceComposition) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/
+ true);
+ EXPECT_EQ(Hwc2::IComposerClient::Composition::DEVICE,
+ mOutputLayer.getState().hwc->hwcCompositionType);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index e80100c..6677f40 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -30,6 +30,7 @@
#include <ui/Region.h>
#include <cmath>
+#include <cstdint>
#include "CallOrderStateMachineHelper.h"
#include "MockHWC2.h"
@@ -237,6 +238,49 @@
}
/*
+ * Output::setLayerCachingEnabled()
+ */
+
+TEST_F(OutputTest, setLayerCachingEnabled_enablesCaching) {
+ const auto kSize = ui::Size(1, 1);
+ EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+ mOutput->setLayerCachingEnabled(false);
+ mOutput->setLayerCachingEnabled(true);
+
+ EXPECT_TRUE(mOutput->plannerEnabled());
+}
+
+TEST_F(OutputTest, setLayerCachingEnabled_disablesCaching) {
+ const auto kSize = ui::Size(1, 1);
+ EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+ mOutput->setLayerCachingEnabled(true);
+ mOutput->setLayerCachingEnabled(false);
+
+ EXPECT_FALSE(mOutput->plannerEnabled());
+}
+
+TEST_F(OutputTest, setLayerCachingEnabled_disablesCachingAndResetsOverrideInfo) {
+ renderengine::mock::RenderEngine renderEngine;
+ const auto kSize = ui::Size(1, 1);
+ EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+ mOutput->setLayerCachingEnabled(true);
+
+ // Inject some layers
+ InjectedLayer layer;
+ layer.outputLayerState.overrideInfo.buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ injectOutputLayer(layer);
+ // inject a null layer to check for null exceptions
+ injectNullOutputLayer();
+
+ EXPECT_NE(nullptr, layer.outputLayerState.overrideInfo.buffer);
+ mOutput->setLayerCachingEnabled(false);
+ EXPECT_EQ(nullptr, layer.outputLayerState.overrideInfo.buffer);
+}
+
+/*
* Output::setProjection()
*/
@@ -783,15 +827,19 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer1.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer2.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer3.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -813,15 +861,19 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer,
- writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
- writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
- writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -842,15 +894,19 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -866,6 +922,64 @@
mOutput->writeCompositionState(args);
}
+TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) {
+ renderengine::mock::RenderEngine renderEngine;
+ InjectedLayer layer0;
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
+
+ InSequence seq;
+ EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+
+ uint32_t z = 0;
+ EXPECT_CALL(*layer0.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+
+ // After calling planComposition (which clears overrideInfo), this test sets
+ // layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it
+ // comes first, setting isPeekingThrough to true and zIsOverridden to true
+ // for it and the following layers.
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/
+ true));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+
+ injectOutputLayer(layer0);
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = true;
+ args.devOptForceClientComposition = false;
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+
+ std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ layer1.outputLayerState.overrideInfo.buffer = buffer;
+ layer2.outputLayerState.overrideInfo.buffer = buffer;
+ layer1.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer;
+ layer2.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer;
+
+ mOutput->writeCompositionState(args);
+}
+
/*
* Output::prepareFrame()
*/
@@ -901,9 +1015,7 @@
mOutput.editState().usesDeviceComposition = true;
EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
- if (mOutput.plannerEnabled()) {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- }
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
mOutput.prepareFrame();
@@ -1144,11 +1256,6 @@
EXPECT_CALL(mOutput, finalizePendingOutputLayers());
mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
-
- // Ensure all output layers have been assigned a simple/flattened z-order.
- EXPECT_EQ(0u, mLayer1.outputLayerState.z);
- EXPECT_EQ(1u, mLayer2.outputLayerState.z);
- EXPECT_EQ(2u, mLayer3.outputLayerState.z);
}
/*
@@ -3501,7 +3608,8 @@
EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(mLayer.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
@@ -4080,16 +4188,20 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -4112,16 +4224,20 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
BlurRegion region;
layer2.layerFEState.blurRegions.push_back(region);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 283c692..8f44677 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -19,8 +19,12 @@
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
+#include <gmock/gmock-actions.h>
#include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+#include <ui/GraphicTypes.h>
+#include <memory>
namespace android::compositionengine {
using namespace std::chrono_literals;
@@ -105,7 +109,6 @@
}
void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) {
- EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion()));
@@ -131,6 +134,7 @@
EXPECT_NE(nullptr, cachedSet.getBuffer());
EXPECT_NE(nullptr, cachedSet.getDrawFence());
EXPECT_TRUE(cachedSet.hasReadyBuffer());
+ EXPECT_TRUE(cachedSet.hasRenderedBuffer());
}
TEST_F(CachedSetTest, createFromLayer) {
@@ -154,7 +158,6 @@
CachedSet cachedSet(layer1);
cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
- EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
Region expectedRegion;
@@ -243,7 +246,6 @@
cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
cachedSet1.append(cachedSet2);
- EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
Region expectedRegion;
@@ -287,10 +289,11 @@
}
TEST_F(CachedSetTest, render) {
- CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
- sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
- CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
- sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+ // 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));
@@ -307,7 +310,7 @@
const std::vector<const renderengine::LayerSettings*>& layers,
const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
base::unique_fd&&, base::unique_fd*) -> size_t {
- EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay);
EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
displaySettings.orientation);
@@ -324,15 +327,271 @@
cachedSet.render(mRenderEngine, mOutputState);
expectReadyBuffer(cachedSet);
- EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getOutputSpace().content);
- EXPECT_EQ(Rect(mOutputState.framebufferSpace.bounds.getWidth(),
- mOutputState.framebufferSpace.bounds.getHeight()),
- cachedSet.getOutputSpace().bounds);
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
// 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();
+ 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({});
+ clientCompList1[0].alpha = 0.5f;
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ clientCompList2[0].alpha = 0.75f;
+
+ mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> size_t {
+ EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ displaySettings.orientation);
+ EXPECT_EQ(0.5f, layers[0]->alpha);
+ EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mOutputState);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+
+ // 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, holePunch_requiresBuffer) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ EXPECT_CALL(*layerFE1, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresRoundedCorners) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+
+ CachedSet cachedSet(layer1);
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresSingleLayer) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(layer2);
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, requiresHolePunch) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+ CachedSet cachedSet(layer);
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_TRUE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresDeviceComposition) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+ auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
+ layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ layerFECompositionState.forceClientComposition = true;
+
+ CachedSet cachedSet(layer);
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, addHolePunch_requiresOverlap) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_requiresOpaque) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.isOpaque = false;
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, false);
+
+ ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_opaque) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.isOpaque = true;
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, false);
+
+ ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_firstLayer) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.isOpaque = false;
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
+ clientCompList3.push_back({});
+
+ clientCompList3[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
+ renderengine::ExternalTexture::READABLE);
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> size_t {
+ // If the highlight layer is enabled, it will increase the size by 1.
+ // We're interested in the third layer either way.
+ EXPECT_GE(layers.size(), 3u);
+ const auto* holePunchSettings = layers[2];
+ EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
+ EXPECT_TRUE(holePunchSettings->disableBlending);
+ EXPECT_EQ(0.0f, holePunchSettings->alpha);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mOutputState);
+}
+
+TEST_F(CachedSetTest, decompose_removesHolePunch) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+
+ std::vector<CachedSet> decomposed = cachedSet.decompose();
+ EXPECT_EQ(2u, decomposed.size());
+ for (const auto& set : decomposed) {
+ EXPECT_EQ(nullptr, set.getHolePunchLayer());
+ }
+}
+
+TEST_F(CachedSetTest, hasBlurBehind) {
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ mTestLayers[1]->layerState->update(&mTestLayers[1]->outputLayer);
+ mTestLayers[2]->layerFECompositionState.blurRegions.push_back(
+ BlurRegion{1, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet1(layer1);
+ CachedSet cachedSet2(layer2);
+ CachedSet cachedSet3(layer3);
+
+ // Cached set 4 will consist of layers 1 and 2, which will contain a blur behind
+ CachedSet cachedSet4(layer1);
+ cachedSet4.addLayer(layer2.getState(), kStartTime);
+
+ EXPECT_FALSE(cachedSet1.hasBlurBehind());
+ EXPECT_TRUE(cachedSet2.hasBlurBehind());
+ EXPECT_TRUE(cachedSet3.hasBlurBehind());
+ EXPECT_TRUE(cachedSet4.hasBlurBehind());
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index c528087..7ec2c98 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -18,10 +18,11 @@
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
#include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
#include <renderengine/mock/RenderEngine.h>
namespace android::compositionengine {
@@ -29,7 +30,6 @@
using impl::planner::Flattener;
using impl::planner::LayerState;
using impl::planner::NonBufferHash;
-using impl::planner::Predictor;
using testing::_;
using testing::ByMove;
@@ -45,7 +45,7 @@
class FlattenerTest : public testing::Test {
public:
- FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {}
+ FlattenerTest() : mFlattener(std::make_unique<Flattener>(true)) {}
void SetUp() override;
protected:
@@ -53,10 +53,6 @@
void initializeFlattener(const std::vector<const LayerState*>& layers);
void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
- // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
- // mPredictor should be mocked and checked for expectations.
- Predictor mPredictor;
-
// mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
renderengine::mock::RenderEngine mRenderEngine;
std::unique_ptr<Flattener> mFlattener;
@@ -275,11 +271,7 @@
mTime += 200ms;
expectAllLayersFlattened(layers);
- EXPECT_EQ(overrideDisplaySpace.bounds,
- Rect(mOutputState.framebufferSpace.bounds.getWidth(),
- mOutputState.framebufferSpace.bounds.getHeight()));
- EXPECT_EQ(overrideDisplaySpace.content, Rect(0, 0, 2, 2));
- EXPECT_EQ(overrideDisplaySpace.orientation, mOutputState.framebufferSpace.orientation);
+ EXPECT_EQ(overrideDisplaySpace, mOutputState.framebufferSpace);
}
TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
@@ -532,5 +524,307 @@
EXPECT_EQ(overrideBuffer4, overrideBuffer5);
}
+// Tests for a PIP
+TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) {
+ 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;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // 3 has a buffer update, so it will not be merged, but it has no round
+ // corners, so it is not a PIP.
+ mTime += 200ms;
+ layerState3->resetFramesSinceBufferUpdate();
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_pip) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ 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;
+
+ EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+ clientCompositionList[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer,
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE);
+ EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(clientCompositionList));
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // 3 has a buffer update, so it will not be merged, and it has round
+ // corners, so it is a PIP.
+ mTime += 200ms;
+ layerState3->resetFramesSinceBufferUpdate();
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ const auto* peekThroughLayer1 =
+ layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ const auto* peekThroughLayer2 =
+ layerState2->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ EXPECT_EQ(&mTestLayers[2]->outputLayer, peekThroughLayer1);
+ EXPECT_EQ(peekThroughLayer1, peekThroughLayer2);
+}
+
+TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ layerState2->update(&mTestLayers[1]->outputLayer);
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the first two layers inactive, which contain the blur behind
+ mTime += 200ms;
+ layerState3->resetFramesSinceBufferUpdate();
+
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // the new flattened layer is replaced
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ layerState2->update(&mTestLayers[1]->outputLayer);
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the last two layers inactive, which contains the blur layer, but does not contain the
+ // first layer
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate();
+
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // nothing is flattened because the last two frames cannot be cached due to containing a blur
+ // layer
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+}
+
+TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+
+ auto& layerStateWithBlurBehind = mTestLayers[1]->layerState;
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ layerStateWithBlurBehind->update(&mTestLayers[1]->outputLayer);
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ auto& layerState4 = mTestLayers[3]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& blurOverrideBuffer =
+ layerStateWithBlurBehind->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerStateWithBlurBehind.get(),
+ layerState3.get(),
+ layerState4.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the last three layers inactive, which contains the blur layer, but does not contain the
+ // first layer
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate();
+
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // the new flattened layer is replaced
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, blurOverrideBuffer);
+ EXPECT_NE(nullptr, overrideBuffer3);
+ EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+}
+
+TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the layers inactive
+ mTime += 200ms;
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+
+ // Simulate attempting to render prior to merging the new cached set with the layer stack.
+ // Here we should not try to re-render.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ // We provide the override buffer now that it's rendered
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer1);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 83cc19b..a09ce14 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -41,8 +41,6 @@
const Rect sRectTwo = Rect(40, 30, 20, 10);
const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
-const constexpr int32_t sZOne = 100;
-const constexpr int32_t sZTwo = 101;
const constexpr float sAlphaOne = 0.25f;
const constexpr float sAlphaTwo = 0.5f;
const Region sRegionOne = Region(sRectOne);
@@ -60,6 +58,10 @@
const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{
.value = std::vector<uint8_t>({1, 3}),
};
+const constexpr int32_t sBgBlurRadiusOne = 3;
+const constexpr int32_t sBgBlurRadiusTwo = 4;
+const BlurRegion sBlurRegionOne = BlurRegion{1, 2.f, 3.f, 4.f, 5.f, 6.f, 7, 8, 9, 10};
+const BlurRegion sBlurRegionTwo = BlurRegion{2, 3.f, 4.f, 5.f, 6.f, 7.f, 8, 9, 10, 11};
struct LayerStateTest : public testing::Test {
LayerStateTest() {
@@ -408,45 +410,6 @@
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
}
-TEST_F(LayerStateTest, updateZOrder) {
- OutputLayerCompositionState outputLayerCompositionState;
- outputLayerCompositionState.z = sZOne;
- LayerFECompositionState layerFECompositionState;
- setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
- layerFECompositionState);
- mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-
- mock::OutputLayer newOutputLayer;
- mock::LayerFE newLayerFE;
- OutputLayerCompositionState outputLayerCompositionStateTwo;
- outputLayerCompositionStateTwo.z = sZTwo;
- setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
- layerFECompositionState);
- Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
- EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder), updates);
-}
-
-TEST_F(LayerStateTest, compareZOrder) {
- OutputLayerCompositionState outputLayerCompositionState;
- outputLayerCompositionState.z = sZOne;
- LayerFECompositionState layerFECompositionState;
- setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
- layerFECompositionState);
- mLayerState = std::make_unique<LayerState>(&mOutputLayer);
- mock::OutputLayer newOutputLayer;
- mock::LayerFE newLayerFE;
- OutputLayerCompositionState outputLayerCompositionStateTwo;
- outputLayerCompositionStateTwo.z = sZTwo;
- setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
- layerFECompositionState);
- auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
-
- verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ZOrder);
-
- EXPECT_TRUE(mLayerState->compare(*otherLayerState));
- EXPECT_TRUE(otherLayerState->compare(*mLayerState));
-}
-
TEST_F(LayerStateTest, updateBufferTransform) {
OutputLayerCompositionState outputLayerCompositionState;
outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
@@ -871,6 +834,114 @@
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
}
+TEST_F(LayerStateTest, updateBackgroundBlur) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BackgroundBlurRadius), updates);
+}
+
+TEST_F(LayerStateTest, compareBackgroundBlur) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ LayerStateField::BackgroundBlurRadius);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBlurRegions) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlurRegions), updates);
+}
+
+TEST_F(LayerStateTest, compareBlurRegions) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlurRegions);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_noBlur_returnsFalse) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_FALSE(mLayerState->hasBlurBehind());
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_withBackgroundBlur_returnsTrue) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_TRUE(mLayerState->hasBlurBehind());
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_withBlurRegion_returnsTrue) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_TRUE(mLayerState->hasBlurBehind());
+}
+
TEST_F(LayerStateTest, dumpDoesNotCrash) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
@@ -954,4 +1025,4 @@
}
} // namespace
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 43e119f..1492707 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -31,8 +31,6 @@
const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
const Rect sRectOne = Rect(1, 2, 3, 4);
const Rect sRectTwo = Rect(4, 3, 2, 1);
-const constexpr int32_t sZOne = 100;
-const constexpr int32_t sZTwo = 101;
const constexpr float sAlphaOne = 0.25f;
const constexpr float sAlphaTwo = 0.5f;
const Region sRegionOne = Region(sRectOne);
@@ -194,11 +192,11 @@
.displayFrame = sRectOne,
.sourceCrop = sFloatRectOne,
.dataspace = ui::Dataspace::SRGB,
- .z = sZOne,
};
LayerFECompositionState layerFECompositionStateOne;
layerFECompositionStateOne.alpha = sAlphaOne;
layerFECompositionStateOne.colorTransformIsIdentity = true;
+ layerFECompositionStateOne.blendMode = hal::BlendMode::NONE;
setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
layerFECompositionStateOne);
LayerState layerStateOne(&outputLayerOne);
@@ -210,12 +208,12 @@
.displayFrame = sRectTwo,
.sourceCrop = sFloatRectTwo,
.dataspace = ui::Dataspace::DISPLAY_P3,
- .z = sZTwo,
};
LayerFECompositionState layerFECompositionStateTwo;
layerFECompositionStateTwo.alpha = sAlphaTwo;
layerFECompositionStateTwo.colorTransformIsIdentity = false;
layerFECompositionStateTwo.colorTransform = sMat4One;
+ layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
layerFECompositionStateTwo);
LayerState layerStateTwo(&outputLayerTwo);
@@ -264,7 +262,6 @@
.displayFrame = sRectOne,
.sourceCrop = sFloatRectOne,
.dataspace = ui::Dataspace::SRGB,
- .z = sZOne,
};
LayerFECompositionState layerFECompositionStateOne;
layerFECompositionStateOne.buffer = new GraphicBuffer();
@@ -282,7 +279,6 @@
.displayFrame = sRectTwo,
.sourceCrop = sFloatRectTwo,
.dataspace = ui::Dataspace::DISPLAY_P3,
- .z = sZTwo,
};
LayerFECompositionState layerFECompositionStateTwo;
layerFECompositionStateTwo.buffer = new GraphicBuffer();
@@ -346,6 +342,32 @@
}
};
+TEST_F(LayerStackTest, reorderingChangesNonBufferHash) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ NonBufferHash hash = getNonBufferHash({&layerStateOne, &layerStateTwo});
+ NonBufferHash hashReverse = getNonBufferHash({&layerStateTwo, &layerStateOne});
+ EXPECT_NE(hash, hashReverse);
+}
+
TEST_F(PredictionTest, constructPrediction) {
Plan plan;
plan.addLayerType(hal::Composition::DEVICE);
@@ -525,4 +547,4 @@
}
} // namespace
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 8692ee6..0f18235 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -138,6 +138,10 @@
getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
}
+void DisplayDevice::enableLayerCaching(bool enable) {
+ getCompositionDisplay()->setLayerCachingEnabled(enable);
+}
+
hal::PowerMode DisplayDevice::getPowerMode() const {
return mPowerMode;
}
@@ -268,8 +272,9 @@
StringAppendF(&result, "+ %s\n", getDebugName().c_str());
StringAppendF(&result, " powerMode=%s (%d)\n", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
+ const auto activeMode = getActiveMode();
StringAppendF(&result, " activeMode=%s\n",
- mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none");
+ activeMode ? to_string(*activeMode).c_str() : "none");
result.append(" supportedModes=\n");
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 68846d3..bf249cd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -157,6 +157,9 @@
void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
bool isPoweredOn() const;
+ // Enables layer caching on this DisplayDevice
+ void enableLayerCaching(bool enable);
+
ui::Dataspace getCompositionDataSpace() const;
/* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index fd70988..1cbcf59 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -25,7 +25,6 @@
#include "ComposerHal.h"
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
-#include <gui/BufferQueue.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
#include <log/log.h>
@@ -266,15 +265,15 @@
Error Composer::createLayer(Display display, Layer* outLayer)
{
Error error = kDefaultError;
- mClient->createLayer(display, BufferQueue::NUM_BUFFER_SLOTS,
- [&](const auto& tmpError, const auto& tmpLayer) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ mClient->createLayer(display, kMaxLayerBufferCount,
+ [&](const auto& tmpError, const auto& tmpLayer) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
- *outLayer = tmpLayer;
- });
+ *outLayer = tmpLayer;
+ });
return error;
}
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c756d65..0619b8c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,6 +32,7 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
+#include <gui/BufferQueue.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/DisplayedFrameStats.h>
@@ -491,6 +492,11 @@
// 64KiB minus a small space for metadata such as read/write pointers
static constexpr size_t kWriterInitialSize =
64 * 1024 / sizeof(uint32_t) - 16;
+ // Max number of buffers that may be cached for a given layer
+ // We obtain this number by:
+ // 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;
CommandWriter mWriter;
CommandReader mReader;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index d04b5f7..27146ab 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -78,7 +78,19 @@
}
Display::~Display() {
- mLayers.clear();
+ // Note: The calls to onOwningDisplayDestroyed() are allowed (and expected)
+ // to call Display::onLayerDestroyed(). As that call removes entries from
+ // mLayers, we do not want to have a for loop directly over it here. Since
+ // the end goal is an empty mLayers anyway, we just go ahead and swap an
+ // initially empty local container with mLayers, and then enumerate
+ // the contents of the local container.
+ Layers destroyingLayers;
+ std::swap(mLayers, destroyingLayers);
+ for (const auto& [_, weakLayer] : destroyingLayers) {
+ if (std::shared_ptr layer = weakLayer.lock()) {
+ layer->onOwningDisplayDestroyed();
+ }
+ }
Error error = Error::NONE;
const char* msg;
@@ -110,29 +122,21 @@
return static_cast<Error>(intError);
}
-Error Display::createLayer(HWC2::Layer** outLayer) {
- if (!outLayer) {
- return Error::BAD_PARAMETER;
- }
+base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> Display::createLayer() {
HWLayerId layerId = 0;
auto intError = mComposer.createLayer(mId, &layerId);
auto error = static_cast<Error>(intError);
if (error != Error::NONE) {
- return error;
+ return base::unexpected(error);
}
- auto layer = std::make_unique<impl::Layer>(mComposer, mCapabilities, mId, layerId);
- *outLayer = layer.get();
- mLayers.emplace(layerId, std::move(layer));
- return Error::NONE;
+ auto layer = std::make_shared<impl::Layer>(mComposer, mCapabilities, *this, layerId);
+ mLayers.emplace(layerId, layer);
+ return layer;
}
-Error Display::destroyLayer(HWC2::Layer* layer) {
- if (!layer) {
- return Error::BAD_PARAMETER;
- }
- mLayers.erase(layer->getId());
- return Error::NONE;
+void Display::onLayerDestroyed(hal::HWLayerId layerId) {
+ mLayers.erase(layerId);
}
bool Display::isVsyncPeriodSwitchSupported() const {
@@ -161,7 +165,7 @@
auto type = types[element];
ALOGV("getChangedCompositionTypes: adding %" PRIu64 " %s",
layer->getId(), to_string(type).c_str());
- outTypes->emplace(layer, type);
+ outTypes->emplace(layer.get(), type);
} else {
ALOGE("getChangedCompositionTypes: invalid layer %" PRIu64 " found"
" on display %" PRIu64, layerIds[element], mId);
@@ -254,7 +258,7 @@
if (layer) {
auto layerRequest =
static_cast<LayerRequest>(layerRequests[element]);
- outLayerRequests->emplace(layer, layerRequest);
+ outLayerRequests->emplace(layer.get(), layerRequest);
} else {
ALOGE("getRequests: invalid layer %" PRIu64 " found on display %"
PRIu64, layerIds[element], mId);
@@ -340,7 +344,7 @@
auto layer = getLayerById(layerIds[element]);
if (layer) {
sp<Fence> fence(new Fence(fenceFds[element]));
- releaseFences.emplace(layer, fence);
+ releaseFences.emplace(layer.get(), fence);
} else {
ALOGE("getReleaseFences: invalid layer %" PRIu64
" found on display %" PRIu64, layerIds[element], mId);
@@ -550,12 +554,9 @@
// Other Display methods
-HWC2::Layer* Display::getLayerById(HWLayerId id) const {
- if (mLayers.count(id) == 0) {
- return nullptr;
- }
-
- return mLayers.at(id).get();
+std::shared_ptr<HWC2::Layer> Display::getLayerById(HWLayerId id) const {
+ auto it = mLayers.find(id);
+ return it != mLayers.end() ? it->second.lock() : nullptr;
}
} // namespace impl
@@ -566,47 +567,78 @@
namespace impl {
Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
- HWDisplayId displayId, HWLayerId layerId)
+ HWC2::Display& display, HWLayerId layerId)
: mComposer(composer),
mCapabilities(capabilities),
- mDisplayId(displayId),
+ mDisplay(&display),
mId(layerId),
mColorMatrix(android::mat4()) {
- ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId);
+ ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, display.getId());
}
Layer::~Layer()
{
- auto intError = mComposer.destroyLayer(mDisplayId, mId);
+ onOwningDisplayDestroyed();
+}
+
+void Layer::onOwningDisplayDestroyed() {
+ // Note: onOwningDisplayDestroyed() may be called to perform cleanup by
+ // either the Layer dtor or by the Display dtor and must be safe to call
+ // from either path. In particular, the call to Display::onLayerDestroyed()
+ // is expected to be safe to do,
+
+ if (CC_UNLIKELY(!mDisplay)) {
+ return;
+ }
+
+ mDisplay->onLayerDestroyed(mId);
+
+ // Note: If the HWC display was actually disconnected, these calls are will
+ // return an error. We always make them as there may be other reasons for
+ // the HWC2::Display to be destroyed.
+ auto intError = mComposer.destroyLayer(mDisplay->getId(), mId);
auto error = static_cast<Error>(intError);
ALOGE_IF(error != Error::NONE,
"destroyLayer(%" PRIu64 ", %" PRIu64 ")"
" failed: %s (%d)",
- mDisplayId, mId, to_string(error).c_str(), intError);
+ mDisplay->getId(), mId, to_string(error).c_str(), intError);
+
+ mDisplay = nullptr;
}
Error Layer::setCursorPosition(int32_t x, int32_t y)
{
- auto intError = mComposer.setCursorPosition(mDisplayId, mId, x, y);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setCursorPosition(mDisplay->getId(), mId, x, y);
return static_cast<Error>(intError);
}
Error Layer::setBuffer(uint32_t slot, const sp<GraphicBuffer>& buffer,
const sp<Fence>& acquireFence)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (buffer == nullptr && mBufferSlot == slot) {
return Error::NONE;
}
mBufferSlot = slot;
int32_t fenceFd = acquireFence->dup();
- auto intError = mComposer.setLayerBuffer(mDisplayId, mId, slot, buffer,
- fenceFd);
+ auto intError = mComposer.setLayerBuffer(mDisplay->getId(), mId, slot, buffer, fenceFd);
return static_cast<Error>(intError);
}
Error Layer::setSurfaceDamage(const Region& damage)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (damage.isRect() && mDamageRegion.isRect() &&
(damage.getBounds() == mDamageRegion.getBounds())) {
return Error::NONE;
@@ -617,8 +649,8 @@
// rects for HWC
Hwc2::Error intError = Hwc2::Error::NONE;
if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) {
- intError = mComposer.setLayerSurfaceDamage(mDisplayId,
- mId, std::vector<Hwc2::IComposerClient::Rect>());
+ intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId,
+ std::vector<Hwc2::IComposerClient::Rect>());
} else {
size_t rectCount = 0;
auto rectArray = damage.getArray(&rectCount);
@@ -629,7 +661,7 @@
rectArray[rect].right, rectArray[rect].bottom});
}
- intError = mComposer.setLayerSurfaceDamage(mDisplayId, mId, hwcRects);
+ intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId, hwcRects);
}
return static_cast<Error>(intError);
@@ -637,34 +669,54 @@
Error Layer::setBlendMode(BlendMode mode)
{
- auto intError = mComposer.setLayerBlendMode(mDisplayId, mId, mode);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerBlendMode(mDisplay->getId(), mId, mode);
return static_cast<Error>(intError);
}
Error Layer::setColor(Color color) {
- auto intError = mComposer.setLayerColor(mDisplayId, mId, color);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerColor(mDisplay->getId(), mId, color);
return static_cast<Error>(intError);
}
Error Layer::setCompositionType(Composition type)
{
- auto intError = mComposer.setLayerCompositionType(mDisplayId, mId, type);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerCompositionType(mDisplay->getId(), mId, type);
return static_cast<Error>(intError);
}
Error Layer::setDataspace(Dataspace dataspace)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (dataspace == mDataSpace) {
return Error::NONE;
}
mDataSpace = dataspace;
- auto intError = mComposer.setLayerDataspace(mDisplayId, mId, mDataSpace);
+ auto intError = mComposer.setLayerDataspace(mDisplay->getId(), mId, mDataSpace);
return static_cast<Error>(intError);
}
Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
const android::HdrMetadata& metadata)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (metadata == mHdrMetadata) {
return Error::NONE;
}
@@ -705,7 +757,7 @@
}
Error error = static_cast<Error>(
- mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
+ mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas));
if (validTypes & HdrMetadata::HDR10PLUS) {
if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) {
@@ -715,8 +767,9 @@
std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
perFrameMetadataBlobs.push_back(
{Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
- Error setMetadataBlobsError = static_cast<Error>(
- mComposer.setLayerPerFrameMetadataBlobs(mDisplayId, mId, perFrameMetadataBlobs));
+ Error setMetadataBlobsError =
+ static_cast<Error>(mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId,
+ perFrameMetadataBlobs));
if (error == Error::NONE) {
return setMetadataBlobsError;
}
@@ -726,46 +779,70 @@
Error Layer::setDisplayFrame(const Rect& frame)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
Hwc2::IComposerClient::Rect hwcRect{frame.left, frame.top,
frame.right, frame.bottom};
- auto intError = mComposer.setLayerDisplayFrame(mDisplayId, mId, hwcRect);
+ auto intError = mComposer.setLayerDisplayFrame(mDisplay->getId(), mId, hwcRect);
return static_cast<Error>(intError);
}
Error Layer::setPlaneAlpha(float alpha)
{
- auto intError = mComposer.setLayerPlaneAlpha(mDisplayId, mId, alpha);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerPlaneAlpha(mDisplay->getId(), mId, alpha);
return static_cast<Error>(intError);
}
Error Layer::setSidebandStream(const native_handle_t* stream)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (mCapabilities.count(Capability::SIDEBAND_STREAM) == 0) {
ALOGE("Attempted to call setSidebandStream without checking that the "
"device supports sideband streams");
return Error::UNSUPPORTED;
}
- auto intError = mComposer.setLayerSidebandStream(mDisplayId, mId, stream);
+ auto intError = mComposer.setLayerSidebandStream(mDisplay->getId(), mId, stream);
return static_cast<Error>(intError);
}
Error Layer::setSourceCrop(const FloatRect& crop)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
Hwc2::IComposerClient::FRect hwcRect{
crop.left, crop.top, crop.right, crop.bottom};
- auto intError = mComposer.setLayerSourceCrop(mDisplayId, mId, hwcRect);
+ auto intError = mComposer.setLayerSourceCrop(mDisplay->getId(), mId, hwcRect);
return static_cast<Error>(intError);
}
Error Layer::setTransform(Transform transform)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
auto intTransform = static_cast<Hwc2::Transform>(transform);
- auto intError = mComposer.setLayerTransform(mDisplayId, mId, intTransform);
+ auto intError = mComposer.setLayerTransform(mDisplay->getId(), mId, intTransform);
return static_cast<Error>(intError);
}
Error Layer::setVisibleRegion(const Region& region)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (region.isRect() && mVisibleRegion.isRect() &&
(region.getBounds() == mVisibleRegion.getBounds())) {
return Error::NONE;
@@ -781,22 +858,30 @@
rectArray[rect].right, rectArray[rect].bottom});
}
- auto intError = mComposer.setLayerVisibleRegion(mDisplayId, mId, hwcRects);
+ auto intError = mComposer.setLayerVisibleRegion(mDisplay->getId(), mId, hwcRects);
return static_cast<Error>(intError);
}
Error Layer::setZOrder(uint32_t z)
{
- auto intError = mComposer.setLayerZOrder(mDisplayId, mId, z);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerZOrder(mDisplay->getId(), mId, z);
return static_cast<Error>(intError);
}
// Composer HAL 2.3
Error Layer::setColorTransform(const android::mat4& matrix) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (matrix == mColorMatrix) {
return Error::NONE;
}
- auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray());
+ auto intError = mComposer.setLayerColorTransform(mDisplay->getId(), mId, matrix.asArray());
Error error = static_cast<Error>(intError);
if (error != Error::NONE) {
return error;
@@ -808,7 +893,12 @@
// Composer HAL 2.4
Error Layer::setLayerGenericMetadata(const std::string& name, bool mandatory,
const std::vector<uint8_t>& value) {
- auto intError = mComposer.setLayerGenericMetadata(mDisplayId, mId, name, mandatory, value);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError =
+ mComposer.setLayerGenericMetadata(mDisplay->getId(), mId, name, mandatory, value);
return static_cast<Error>(intError);
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index e7bf286..fae95e7 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/expected.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/HdrCapabilities.h>
@@ -84,10 +85,11 @@
virtual void setConnected(bool connected) = 0; // For use by Device only
virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0;
virtual bool isVsyncPeriodSwitchSupported() const = 0;
+ virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
[[clang::warn_unused_result]] virtual hal::Error acceptChanges() = 0;
- [[clang::warn_unused_result]] virtual hal::Error createLayer(Layer** outLayer) = 0;
- [[clang::warn_unused_result]] virtual hal::Error destroyLayer(Layer* layer) = 0;
+ [[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;
[[clang::warn_unused_result]] virtual hal::Error getColorModes(
@@ -152,6 +154,8 @@
namespace impl {
+class Layer;
+
class Display : public HWC2::Display {
public:
Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId,
@@ -160,10 +164,9 @@
// Required by HWC2
hal::Error acceptChanges() override;
- hal::Error createLayer(Layer** outLayer) override;
- hal::Error destroyLayer(Layer*) override;
+ base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> createLayer() override;
hal::Error getChangedCompositionTypes(
- std::unordered_map<Layer*, hal::Composition>* outTypes) override;
+ std::unordered_map<HWC2::Layer*, hal::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;
@@ -174,7 +177,7 @@
hal::Error getName(std::string* outName) const override;
hal::Error getRequests(
hal::DisplayRequest* outDisplayRequests,
- std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) override;
+ std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override;
hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
hal::Error supportsDoze(bool* outSupport) const override;
hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
@@ -185,8 +188,8 @@
uint64_t maxFrames) const override;
hal::Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
android::DisplayedFrameStats* outStats) const override;
- hal::Error getReleaseFences(
- std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
+ hal::Error getReleaseFences(std::unordered_map<HWC2::Layer*, android::sp<android::Fence>>*
+ outFences) const override;
hal::Error present(android::sp<android::Fence>* outPresentFence) override;
hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence,
@@ -218,13 +221,14 @@
const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override {
return mDisplayCapabilities;
};
- virtual bool isVsyncPeriodSwitchSupported() const override;
+ bool isVsyncPeriodSwitchSupported() const override;
+ void onLayerDestroyed(hal::HWLayerId layerId) override;
private:
// This may fail (and return a null pointer) if no layer with this ID exists
// on this display
- Layer* getLayerById(hal::HWLayerId) const;
+ std::shared_ptr<HWC2::Layer> getLayerById(hal::HWLayerId id) const;
friend android::TestableSurfaceFlinger;
@@ -240,7 +244,8 @@
hal::DisplayType mType;
bool mIsConnected = false;
- std::unordered_map<hal::HWLayerId, std::unique_ptr<Layer>> mLayers;
+ using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>;
+ Layers mLayers;
std::once_flag mDisplayCapabilityQueryFlag;
std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
@@ -295,10 +300,12 @@
class Layer : public HWC2::Layer {
public:
Layer(android::Hwc2::Composer& composer,
- const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId displayId,
+ const std::unordered_set<hal::Capability>& capabilities, HWC2::Display& display,
hal::HWLayerId layerId);
~Layer() override;
+ void onOwningDisplayDestroyed();
+
hal::HWLayerId getId() const override { return mId; }
hal::Error setCursorPosition(int32_t x, int32_t y) override;
@@ -334,7 +341,7 @@
android::Hwc2::Composer& mComposer;
const std::unordered_set<hal::Capability>& mCapabilities;
- hal::HWDisplayId mDisplayId;
+ HWC2::Display* mDisplay;
hal::HWLayerId mId;
// Cached HWC2 data, to ensure the same commands aren't sent to the HWC
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b73d032..36876dc 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -305,20 +305,15 @@
return value;
}
-HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) {
+std::shared_ptr<HWC2::Layer> HWComposer::createLayer(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
- HWC2::Layer* layer;
- auto error = mDisplayData[displayId].hwcDisplay->createLayer(&layer);
- RETURN_IF_HWC_ERROR(error, displayId, nullptr);
- return layer;
-}
-
-void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) {
- RETURN_IF_INVALID_DISPLAY(displayId);
-
- auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
- RETURN_IF_HWC_ERROR(error, displayId);
+ auto expected = mDisplayData[displayId].hwcDisplay->createLayer();
+ if (!expected.has_value()) {
+ auto error = std::move(expected).error();
+ RETURN_IF_HWC_ERROR(error, displayId, nullptr);
+ }
+ return std::move(expected).value();
}
bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
@@ -471,6 +466,7 @@
status_t HWComposer::getDeviceCompositionChanges(
HalDisplayId displayId, bool frameUsesClientComposition,
+ std::chrono::steady_clock::time_point earliestPresentTime,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -487,12 +483,14 @@
hal::Error error = hal::Error::NONE;
- // First try to skip validate altogether when there is no client
- // composition. When there is client composition, since we haven't
- // rendered to the client target yet, we should not attempt to skip
- // validate.
+ // First try to skip validate altogether when we passed the earliest time
+ // to present and there is no client. Otherwise, we may present a frame too
+ // early or in case of client composition we first need to render the
+ // client target buffer.
+ const bool canSkipValidate =
+ std::chrono::steady_clock::now() >= earliestPresentTime && !frameUsesClientComposition;
displayData.validateWasSkipped = false;
- if (!frameUsesClientComposition) {
+ if (canSkipValidate) {
sp<Fence> outPresentFence;
uint32_t state = UINT32_MAX;
error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
@@ -556,7 +554,8 @@
return fence->second;
}
-status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(
+ HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime) {
ATRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -572,6 +571,11 @@
return NO_ERROR;
}
+ {
+ ATRACE_NAME("wait for earliest present time");
+ std::this_thread::sleep_until(earliestPresentTime);
+ }
+
auto error = hwcDisplay->present(&displayData.lastPresentFence);
RETURN_IF_HWC_ERROR_FOR("present", error, displayId, UNKNOWN_ERROR);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index f532e50..d0c0c11 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -116,9 +116,7 @@
virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
// Attempts to create a new layer on this display
- virtual HWC2::Layer* createLayer(HalDisplayId) = 0;
- // Destroy a previously created layer
- virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0;
+ virtual std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) = 0;
// Gets any required composition change requests from the HWC device.
//
@@ -129,13 +127,15 @@
// expected.
virtual status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
+ std::chrono::steady_clock::time_point earliestPresentTime,
std::optional<DeviceRequestedChanges>* outChanges) = 0;
virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
// Present layers to the display and read releaseFences.
- virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
+ virtual status_t presentAndGetReleaseFences(
+ HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) = 0;
// set power mode
virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -262,19 +262,19 @@
void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
// Attempts to create a new layer on this display
- HWC2::Layer* createLayer(HalDisplayId) override;
- // Destroy a previously created layer
- void destroyLayer(HalDisplayId, HWC2::Layer*) override;
+ std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) override;
status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
+ std::chrono::steady_clock::time_point earliestPresentTime,
std::optional<DeviceRequestedChanges>* outChanges) override;
status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) override;
// Present layers to the display and read releaseFences.
- status_t presentAndGetReleaseFences(HalDisplayId) override;
+ status_t presentAndGetReleaseFences(
+ HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) override;
// set power mode
status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
index caef338..fd18c3b 100644
--- a/services/surfaceflinger/EffectLayer.cpp
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -57,19 +57,15 @@
return {};
}
- std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings =
- prepareShadowClientComposition(*layerSettings, targetSettings.viewport,
- targetSettings.dataspace);
- if (shadowSettings) {
- results.push_back(*shadowSettings);
- }
+ // set the shadow for the layer if needed
+ prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
// If fill bounds are occluded or the fill color is invalid skip the fill settings.
if (targetSettings.realContentIsVisible && fillsColor()) {
// Set color for color fill settings.
layerSettings->source.solidColor = getColor().rgb;
results.push_back(*layerSettings);
- } else if (hasBlur()) {
+ } else if (hasBlur() || drawShadows()) {
results.push_back(*layerSettings);
}
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index 0bc2d3e..23db805 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -26,10 +26,18 @@
namespace android {
-FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger)
- : mFrameTimeline(frameTimeline), mFlinger(flinger) {}
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+ std::unique_ptr<Clock> clock)
+ : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) {
+ LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!");
+}
-void FpsReporter::dispatchLayerFps() const {
+void FpsReporter::dispatchLayerFps() {
+ const auto now = mClock->now();
+ if (now - mLastDispatch < kMinDispatchDuration) {
+ return;
+ }
+
std::vector<TrackedListener> localListeners;
{
std::scoped_lock lock(mMutex);
@@ -71,6 +79,8 @@
listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
}
+
+ mLastDispatch = now;
}
void FpsReporter::binderDied(const wp<IBinder>& who) {
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index 1cec295..bd7b9a5 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -22,6 +22,7 @@
#include <unordered_map>
+#include "Clock.h"
#include "FrameTimeline/FrameTimeline.h"
namespace android {
@@ -31,12 +32,13 @@
class FpsReporter : public IBinder::DeathRecipient {
public:
- FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger);
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+ std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
// Dispatches updated layer fps values for the registered listeners
// This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
// must be held when calling this method.
- void dispatchLayerFps() const EXCLUDES(mMutex);
+ void dispatchLayerFps() EXCLUDES(mMutex);
// Override for IBinder::DeathRecipient
void binderDied(const wp<IBinder>&) override;
@@ -61,6 +63,10 @@
frametimeline::FrameTimeline& mFrameTimeline;
SurfaceFlinger& mFlinger;
+ static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration =
+ std::chrono::milliseconds(500);
+ std::unique_ptr<Clock> mClock;
+ std::chrono::steady_clock::time_point mLastDispatch;
std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
};
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 7468ac3..f19e2a7 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -256,6 +256,10 @@
protoJank |= FrameTimelineEvent::JANK_UNKNOWN;
jankType &= ~JankType::Unknown;
}
+ if (jankType & JankType::SurfaceFlingerStuffing) {
+ protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
+ jankType &= ~JankType::SurfaceFlingerStuffing;
+ }
// jankType should be 0 if all types of jank were checked for.
LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -653,8 +657,10 @@
// If prediction is expired, we can't use the predicted start time. Instead, just use a
// start time a little earlier than the end time so that we have some info about this
// frame in the trace.
+ nsecs_t endTime =
+ (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
packet->set_timestamp(
- static_cast<uint64_t>(mActuals.endTime - kPredictionExpiredStartTimeDelta));
+ static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta));
} else {
packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
}
@@ -724,9 +730,11 @@
int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
ATRACE_CALL();
std::scoped_lock lock(mMutex);
+ while (mPredictions.size() >= kMaxTokens) {
+ mPredictions.erase(mPredictions.begin());
+ }
const int64_t assignedToken = mCurrentToken++;
- mPredictions[assignedToken] = {systemTime(), predictions};
- flushTokens(systemTime());
+ mPredictions[assignedToken] = predictions;
return assignedToken;
}
@@ -734,23 +742,11 @@
std::scoped_lock lock(mMutex);
auto predictionsIterator = mPredictions.find(token);
if (predictionsIterator != mPredictions.end()) {
- return predictionsIterator->second.predictions;
+ return predictionsIterator->second;
}
return {};
}
-void TokenManager::flushTokens(nsecs_t flushTime) {
- for (auto it = mPredictions.begin(); it != mPredictions.end();) {
- if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
- it = mPredictions.erase(it);
- } else {
- // Tokens are ordered by time. If i'th token is within the retention time, then the
- // i+1'th token will also be within retention time.
- break;
- }
- }
-}
-
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
JankClassificationThresholds thresholds)
: mMaxDisplayFrames(kDefaultMaxDisplayFrames),
@@ -873,7 +869,8 @@
mGpuFence = gpuFence;
}
-void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
+void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+ nsecs_t previousPresentTime) {
if (mPredictionState == PredictionState::Expired ||
mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
// Cannot do jank classification with expired predictions or invalid signal times. Set the
@@ -947,7 +944,15 @@
mJankType = JankType::Unknown;
}
} else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
- if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <=
+ mJankClassificationThresholds.presentThreshold ||
+ previousPresentTime > mSurfaceFlingerPredictions.presentTime) {
+ // The previous frame was either presented in the current frame's expected vsync or
+ // it was presented even later than the current frame's expected vsync.
+ mJankType = JankType::SurfaceFlingerStuffing;
+ }
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish &&
+ !(mJankType & JankType::SurfaceFlingerStuffing)) {
// Finish on time, Present late
if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
@@ -961,11 +966,12 @@
mJankType = JankType::PredictionError;
}
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- if (mFrameStartMetadata == FrameStartMetadata::LateStart) {
- // Late start, Late finish, Late Present
- mJankType = JankType::SurfaceFlingerScheduling;
- } else {
- // OnTime start, Finish late, Present late
+ if (!(mJankType & JankType::SurfaceFlingerStuffing) ||
+ mSurfaceFlingerActuals.presentTime - previousPresentTime >
+ mRefreshRate.getPeriodNsecs() +
+ mJankClassificationThresholds.presentThreshold) {
+ // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
+ // was presented more than a vsync late.
if (mGpuFence != FenceTime::NO_FENCE &&
mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime <
mRefreshRate.getPeriodNsecs()) {
@@ -987,11 +993,11 @@
}
}
-void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previousPresentTime) {
mSurfaceFlingerActuals.presentTime = signalTime;
nsecs_t deadlineDelta = 0;
nsecs_t deltaToVsync = 0;
- classifyJank(deadlineDelta, deltaToVsync);
+ classifyJank(deadlineDelta, deltaToVsync, previousPresentTime);
for (auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
@@ -1156,8 +1162,9 @@
}
}
auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime);
+ displayFrame->onPresent(signalTime, mPreviousPresentTime);
displayFrame->trace(mSurfaceFlingerPid);
+ mPreviousPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
--i;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 41f4978..42be55a 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -92,11 +92,6 @@
bool operator!=(const TimelineItem& other) const { return !(*this == other); }
};
-struct TokenManagerPrediction {
- nsecs_t timestamp = 0;
- TimelineItem predictions;
-};
-
struct JankClassificationThresholds {
// The various thresholds for App and SF. If the actual timestamp falls within the threshold
// compared to prediction, we treat it as on time.
@@ -334,11 +329,10 @@
void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
- std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
+ std::map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
int64_t mCurrentToken GUARDED_BY(mMutex);
mutable std::mutex mMutex;
- static constexpr nsecs_t kMaxRetentionTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
+ static constexpr size_t kMaxTokens = 500;
};
class FrameTimeline : public android::frametimeline::FrameTimeline {
@@ -370,7 +364,7 @@
void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
nsecs_t wakeUpTime);
// Sets the appropriate metadata and classifies the jank.
- void onPresent(nsecs_t signalTime);
+ void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
// Adds the provided SurfaceFrame to the current display frame.
void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
@@ -398,7 +392,8 @@
void dump(std::string& result, nsecs_t baseTime) const;
void tracePredictions(pid_t surfaceFlingerPid) const;
void traceActuals(pid_t surfaceFlingerPid) const;
- void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync);
+ void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+ nsecs_t previousPresentTime);
int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
@@ -480,6 +475,7 @@
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
+ nsecs_t mPreviousPresentTime = 0;
const JankClassificationThresholds mJankClassificationThresholds;
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index cf215ad..a7c8704 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -129,10 +129,10 @@
mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
mCurrentState.metadata = args.metadata;
mCurrentState.shadowRadius = 0.f;
- mCurrentState.treeHasFrameRateVote = false;
mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
mCurrentState.frameTimelineInfo = {};
mCurrentState.postTime = -1;
+ mCurrentState.destinationFrame.makeInvalid();
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -221,7 +221,10 @@
}
void Layer::removeFromCurrentState() {
- mRemovedFromCurrentState = true;
+ if (!mRemovedFromCurrentState) {
+ mRemovedFromCurrentState = true;
+ mFlinger->mScheduler->deregisterLayer(this);
+ }
mFlinger->markLayerPendingRemovalLocked(this);
}
@@ -246,7 +249,10 @@
}
void Layer::addToCurrentState() {
- mRemovedFromCurrentState = false;
+ if (mRemovedFromCurrentState) {
+ mRemovedFromCurrentState = false;
+ mFlinger->mScheduler->registerLayer(this);
+ }
for (const auto& child : mCurrentChildren) {
child->addToCurrentState();
@@ -315,55 +321,6 @@
return reduce(mBounds, activeTransparentRegion);
}
-ui::Transform Layer::getBufferScaleTransform() const {
- // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
- // it isFixedSize) then there may be additional scaling not accounted
- // for in the layer transform.
- if (!isFixedSize() || getBuffer() == nullptr) {
- return {};
- }
-
- // If the layer is a buffer state layer, the active width and height
- // could be infinite. In that case, return the effective transform.
- const uint32_t activeWidth = getActiveWidth(getDrawingState());
- const uint32_t activeHeight = getActiveHeight(getDrawingState());
- if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) {
- return {};
- }
-
- int bufferWidth = getBuffer()->getWidth();
- int bufferHeight = getBuffer()->getHeight();
-
- if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
-
- float sx = activeWidth / static_cast<float>(bufferWidth);
- float sy = activeHeight / static_cast<float>(bufferHeight);
-
- ui::Transform extraParentScaling;
- extraParentScaling.set(sx, 0, 0, sy);
- return extraParentScaling;
-}
-
-ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
- // We need to mirror this scaling to child surfaces or we will break the contract where WM can
- // treat child surfaces as pixels in the parent surface.
- if (!isFixedSize() || getBuffer() == nullptr) {
- return mEffectiveTransform;
- }
- return mEffectiveTransform * bufferScaleTransform;
-}
-
-FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
- // We need the pre scaled layer bounds when computing child bounds to make sure the child is
- // cropped to its parent layer after any buffer transform scaling is applied.
- if (!isFixedSize() || getBuffer() == nullptr) {
- return mBounds;
- }
- return bufferScaleTransform.inverse().transform(mBounds);
-}
-
void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
float parentShadowRadius) {
const State& s(getDrawingState());
@@ -400,11 +357,8 @@
// don't pass it to its children.
const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
- // Add any buffer scaling to the layer's children.
- ui::Transform bufferScaleTransform = getBufferScaleTransform();
for (const sp<Layer>& child : mDrawingChildren) {
- child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
- getTransformWithScale(bufferScaleTransform), childShadowRadius);
+ child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
}
}
@@ -531,7 +485,9 @@
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
// Force client composition for special cases known only to the front-end.
- if (isHdrY410() || usesRoundedCorners || drawShadows() || drawingState.blurRegions.size() > 0 ||
+ // Rounded corners no longer force client composition, since we may use a
+ // hole punch so that the layer will appear to have rounded corners.
+ if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 ||
compositionState->stretchEffect.hasEffect()) {
compositionState->forceClientComposition = true;
}
@@ -617,6 +573,9 @@
layerSettings.geometry.boundaries = bounds;
layerSettings.geometry.positionTransform = getTransform().asMatrix4();
+ // skip drawing content if the targetSettings indicate the content will be occluded
+ layerSettings.skipContentDraw = !targetSettings.realContentIsVisible;
+
if (hasColorTransform()) {
layerSettings.colorTransform = getColorTransform();
}
@@ -630,66 +589,15 @@
if (!targetSettings.disableBlurs) {
layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
layerSettings.blurRegions = getBlurRegions();
+ layerSettings.blurRegionTransform =
+ getActiveTransform(getDrawingState()).inverse().asMatrix4();
}
- layerSettings.stretchEffect = getDrawingState().stretchEffect;
+ layerSettings.stretchEffect = getStretchEffect();
// Record the name of the layer for debugging further down the stack.
layerSettings.name = getName();
return layerSettings;
}
-std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition(
- const LayerFE::LayerSettings& casterLayerSettings, const Rect& layerStackRect,
- ui::Dataspace outputDataspace) {
- renderengine::ShadowSettings shadow = getShadowSettings(layerStackRect);
- if (shadow.length <= 0.f) {
- return {};
- }
-
- const float casterAlpha = casterLayerSettings.alpha;
- const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) &&
- casterLayerSettings.source.buffer.isOpaque);
-
- compositionengine::LayerFE::LayerSettings shadowLayer = casterLayerSettings;
-
- shadowLayer.shadow = shadow;
- shadowLayer.geometry.boundaries = mBounds; // ignore transparent region
-
- // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
- // Otherwise the generated shadow will only be shown around the casting layer.
- shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
- shadowLayer.shadow.ambientColor *= casterAlpha;
- shadowLayer.shadow.spotColor *= casterAlpha;
- shadowLayer.sourceDataspace = outputDataspace;
- shadowLayer.source.buffer.buffer = nullptr;
- shadowLayer.source.buffer.fence = nullptr;
- shadowLayer.frameNumber = 0;
- shadowLayer.bufferId = 0;
- shadowLayer.name = getName();
-
- if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
- return {};
- }
-
- float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
- const FloatRect& cornerRadiusCropRect = shadowLayer.geometry.roundedCornersCrop;
- const FloatRect& casterRect = shadowLayer.geometry.boundaries;
-
- // crop used to set the corner radius may be larger than the content rect. Adjust the corner
- // radius accordingly.
- if (casterCornerRadius > 0.f) {
- float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
- std::abs(cornerRadiusCropRect.left - casterRect.left));
- if (cropRectOffset > casterCornerRadius) {
- casterCornerRadius = 0;
- } else {
- casterCornerRadius -= cropRectOffset;
- }
- shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
- }
-
- return shadowLayer;
-}
-
void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
bool blackout) const {
layerSettings.source.buffer.buffer = nullptr;
@@ -703,6 +611,9 @@
layerSettings.name = getName();
}
+// TODO(b/188891810): This method now only ever returns 0 or 1 layers so we should return
+// std::optional instead of a vector. Additionally, we should consider removing
+// this method entirely in favor of calling prepareClientComposition directly.
std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
@@ -718,21 +629,10 @@
return {*layerSettings};
}
- std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings =
- prepareShadowClientComposition(*layerSettings, targetSettings.viewport,
- targetSettings.dataspace);
- // There are no shadows to render.
- if (!shadowSettings) {
- return {*layerSettings};
- }
+ // set the shadow for the layer if needed
+ prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
- // If the layer casts a shadow but the content casting the shadow is occluded, skip
- // composing the non-shadow content and only draw the shadows.
- if (targetSettings.realContentIsVisible) {
- return {*shadowSettings, *layerSettings};
- }
-
- return {*shadowSettings};
+ return {*layerSettings};
}
Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
@@ -854,7 +754,12 @@
const State& s(getDrawingState());
State& c(getCurrentState());
- if (getActiveGeometry(c) != getActiveGeometry(s)) {
+ // Translates dest frame into scale and position updates. This helps align geometry calculations
+ // for BufferStateLayer with other layers. This should ideally happen in the client once client
+ // has the display orientation details from WM.
+ updateGeometry();
+
+ if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
}
@@ -903,8 +808,15 @@
// list.
addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
}
+ const bool frameRateVoteChanged =
+ mDrawingState.frameRateForLayerTree != stateToCommit.frameRateForLayerTree;
mDrawingState = stateToCommit;
+ if (frameRateVoteChanged) {
+ mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+ LayerHistory::LayerUpdateType::SetFrameRate);
+ }
+
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -929,20 +841,18 @@
}
bool Layer::setPosition(float x, float y) {
- if (mCurrentState.requested_legacy.transform.tx() == x &&
- mCurrentState.requested_legacy.transform.ty() == y)
- return false;
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false;
mCurrentState.sequence++;
// We update the requested and active position simultaneously because
// we want to apply the position portion of the transform matrix immediately,
// but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
- mCurrentState.requested_legacy.transform.set(x, y);
+ mCurrentState.transform.set(x, y);
// Here we directly update the active state
// unlike other setters, because we store it within
// the transform, but use different latching rules.
// b/38182305
- mCurrentState.active_legacy.transform.set(x, y);
+ mCurrentState.transform.set(x, y);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -1061,6 +971,7 @@
setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h);
return true;
}
+
bool Layer::setAlpha(float alpha) {
if (mCurrentState.color.a == alpha) return false;
mCurrentState.sequence++;
@@ -1139,8 +1050,7 @@
return false;
}
mCurrentState.sequence++;
- mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx,
- matrix.dsdy);
+ mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1309,8 +1219,7 @@
};
// update parents and children about the vote
- // First traverse the tree and count how many layers has votes. In addition
- // activate the layers in Scheduler's LayerHistory for it to check for changes
+ // First traverse the tree and count how many layers has votes.
int layersWithVote = 0;
traverseTree([&layersWithVote](Layer* layer) {
const auto layerVotedWithDefaultCompatibility =
@@ -1330,20 +1239,11 @@
}
});
- // Now update the other layers
+ // Now we can update the tree frame rate vote for each layer in the tree
+ const bool treeHasFrameRateVote = layersWithVote > 0;
bool transactionNeeded = false;
- traverseTree([layersWithVote, &transactionNeeded, this](Layer* layer) {
- const bool treeHasFrameRateVote = layersWithVote > 0;
- if (layer->mCurrentState.treeHasFrameRateVote != treeHasFrameRateVote) {
- layer->mCurrentState.sequence++;
- layer->mCurrentState.treeHasFrameRateVote = treeHasFrameRateVote;
- layer->mCurrentState.modified = true;
- layer->setTransactionFlags(eTransactionNeeded);
- transactionNeeded = true;
-
- mFlinger->mScheduler->recordLayerHistory(layer, systemTime(),
- LayerHistory::LayerUpdateType::SetFrameRate);
- }
+ traverseTree([treeHasFrameRateVote, &transactionNeeded](Layer* layer) {
+ transactionNeeded = layer->updateFrameRateForLayerTree(treeHasFrameRateVote);
});
if (transactionNeeded) {
@@ -1472,32 +1372,42 @@
return surfaceFrame;
}
-Layer::FrameRate Layer::getFrameRateForLayerTree() const {
- const auto frameRate = getDrawingState().frameRate;
+bool Layer::updateFrameRateForLayerTree(bool treeHasFrameRateVote) {
+ const auto updateCurrentState = [&](FrameRate frameRate) {
+ if (mCurrentState.frameRateForLayerTree == frameRate) {
+ return false;
+ }
+ mCurrentState.frameRateForLayerTree = frameRate;
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+ };
+
+ const auto frameRate = mCurrentState.frameRate;
if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) {
- return frameRate;
+ return updateCurrentState(frameRate);
}
// This layer doesn't have a frame rate. Check if its ancestors have a vote
- if (sp<Layer> parent = getParent(); parent) {
- if (const auto parentFrameRate = parent->getFrameRateForLayerTree();
- parentFrameRate.rate.isValid()) {
- return parentFrameRate;
+ for (sp<Layer> parent = getParent(); parent; parent = parent->getParent()) {
+ if (parent->mCurrentState.frameRate.rate.isValid()) {
+ return updateCurrentState(parent->mCurrentState.frameRate);
}
}
// This layer and its ancestors don't have a frame rate. If one of successors
// has a vote, return a NoVote for successors to set the vote
- if (getDrawingState().treeHasFrameRateVote) {
- return {Fps(0.0f), FrameRateCompatibility::NoVote};
+ if (treeHasFrameRateVote) {
+ return updateCurrentState(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
}
- return frameRate;
+ return updateCurrentState(frameRate);
}
-// ----------------------------------------------------------------------------
-// pageflip handling...
-// ----------------------------------------------------------------------------
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+ return getDrawingState().frameRateForLayerTree;
+}
bool Layer::isHiddenByPolicy() const {
const State& s(mDrawingState);
@@ -1556,20 +1466,20 @@
info.mVisibleRegion = getVisibleRegion(display);
info.mSurfaceDamageRegion = surfaceDamageRegion;
info.mLayerStack = getLayerStack();
- info.mX = ds.active_legacy.transform.tx();
- info.mY = ds.active_legacy.transform.ty();
+ info.mX = ds.transform.tx();
+ info.mY = ds.transform.ty();
info.mZ = ds.z;
- info.mWidth = ds.active_legacy.w;
- info.mHeight = ds.active_legacy.h;
+ info.mWidth = ds.width;
+ info.mHeight = ds.height;
info.mCrop = ds.crop;
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
- info.mMatrix[0][0] = ds.active_legacy.transform[0][0];
- info.mMatrix[0][1] = ds.active_legacy.transform[0][1];
- info.mMatrix[1][0] = ds.active_legacy.transform[1][0];
- info.mMatrix[1][1] = ds.active_legacy.transform[1][1];
+ info.mMatrix[0][0] = ds.transform[0][0];
+ info.mMatrix[0][1] = ds.transform[0][1];
+ info.mMatrix[1][0] = ds.transform[1][0];
+ info.mMatrix[1][1] = ds.transform[1][1];
{
sp<const GraphicBuffer> buffer = getBuffer();
if (buffer != 0) {
@@ -1766,8 +1676,7 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
- child->computeBounds(newParent->mBounds,
- newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+ child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
newParent->mEffectiveShadowRadius);
}
}
@@ -2111,16 +2020,37 @@
: RoundedCornerState();
}
-renderengine::ShadowSettings Layer::getShadowSettings(const Rect& layerStackRect) const {
+void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+ const Rect& layerStackRect) {
renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
+ // Note: this preserves existing behavior of shadowing the entire layer and not cropping it if
+ // transparent regions are present. This may not be necessary since shadows are only cast by
+ // SurfaceFlinger's EffectLayers, which do not typically use transparent regions.
+ state.boundaries = mBounds;
+
// Shift the spot light x-position to the middle of the display and then
// offset it by casting layer's screen pos.
state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
state.lightPos.y -= mScreenBounds.top;
state.length = mEffectiveShadowRadius;
- return state;
+
+ if (state.length > 0.f) {
+ const float casterAlpha = caster.alpha;
+ const bool casterIsOpaque =
+ ((caster.source.buffer.buffer != nullptr) && caster.source.buffer.isOpaque);
+
+ // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
+ // Otherwise the generated shadow will only be shown around the casting layer.
+ state.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+ state.ambientColor *= casterAlpha;
+ state.spotColor *= casterAlpha;
+
+ if (state.ambientColor.a > 0.f && state.spotColor.a > 0.f) {
+ caster.shadow = state;
+ }
+ }
}
void Layer::commitChildList() {
@@ -2445,6 +2375,8 @@
ui::Transform toPhysicalDisplay;
if (display) {
toPhysicalDisplay = display->getTransform();
+ info.displayWidth = display->getWidth();
+ info.displayHeight = display->getHeight();
}
fillInputFrameInfo(info, toPhysicalDisplay);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9f3ea9a..66d7018 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -239,8 +239,8 @@
FrameRate frameRate;
- // Indicates whether parents / children of this layer had set FrameRate
- bool treeHasFrameRateVote;
+ // The combined frame rate of parents / children of this layer
+ FrameRate frameRateForLayerTree;
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -274,6 +274,9 @@
// Stretch effect to apply to this layer
StretchEffect stretchEffect;
+
+ Rect bufferCrop;
+ Rect destinationFrame;
};
/*
@@ -482,12 +485,9 @@
// to avoid grabbing the lock again to avoid deadlock
virtual bool isCreatedFromMainThread() const { return false; }
- virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
- virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
- virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
- virtual ui::Transform getActiveTransform(const Layer::State& s) const {
- return s.active_legacy.transform;
- }
+ uint32_t getActiveWidth(const Layer::State& s) const { return s.width; }
+ uint32_t getActiveHeight(const Layer::State& s) const { return s.height; }
+ ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
virtual Region getActiveTransparentRegion(const Layer::State& s) const {
return s.activeTransparentRegion_legacy;
}
@@ -600,6 +600,8 @@
// ignored.
virtual RoundedCornerState getRoundedCornerState() const;
+ bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+
virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
/**
* Return whether this layer needs an input info. For most layer types
@@ -645,16 +647,6 @@
// Compute bounds for the layer and cache the results.
void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
- // Returns the buffer scale transform if a scaling mode is set.
- ui::Transform getBufferScaleTransform() const;
-
- // Get effective layer transform, taking into account all its parent transform with any
- // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
- ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
-
- // Returns the bounds of the layer without any buffer scaling.
- FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
-
int32_t getSequence() const override { return sequence; }
// For tracing.
@@ -772,8 +764,6 @@
// is ready to acquire a buffer.
ui::Transform::RotationFlags getFixedTransformHint() const;
- renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const;
-
/**
* Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
* which will not emit children who have relativeZOrder to another layer, this method
@@ -883,8 +873,11 @@
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
+ virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
+ virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
virtual std::string getPendingBufferCounterName() { return ""; }
+ virtual void updateGeometry() {}
protected:
friend class impl::SurfaceInterceptor;
@@ -900,9 +893,6 @@
virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&);
- virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
- const LayerFE::LayerSettings&, const Rect& layerStackRect,
- ui::Dataspace outputDataspace);
virtual void preparePerFrameCompositionState();
virtual void commitTransaction(State& stateToCommit);
virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
@@ -928,6 +918,7 @@
// Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
// the settings clears the content with a solid black fill.
void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
+ void prepareShadowClientComposition(LayerFE::LayerSettings& caster, const Rect& layerStackRect);
void prepareBasicGeometryCompositionState();
void prepareGeometryCompositionState();
@@ -1062,6 +1053,8 @@
// Fills in the frame and transform info for the InputWindowInfo
void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
+ bool updateFrameRateForLayerTree(bool treeHasFrameRateVote);
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
// a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 00090d9..653aca6 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -56,16 +56,14 @@
noWorkNeeded,
idleTimerWaiting,
waitForQuietFrame,
- waitForZeroPhase,
waitForSamplePhase,
sample
};
-constexpr auto timeForRegionSampling = 5000000ns;
-constexpr auto maxRegionSamplingSkips = 10;
constexpr auto defaultRegionSamplingWorkDuration = 3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+constexpr auto maxRegionSamplingDelay = 100ms;
// TODO: (b/127403193) duration to string conversion could probably be constexpr
template <typename Rep, typename Per>
inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
@@ -99,97 +97,22 @@
}
}
-struct SamplingOffsetCallback : VSyncSource::Callback {
- SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
- std::chrono::nanoseconds targetSamplingWorkDuration)
- : mRegionSamplingThread(samplingThread),
- mTargetSamplingWorkDuration(targetSamplingWorkDuration),
- mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
- 0ns,
- /*traceVsync=*/false)) {
- mVSyncSource->setCallback(this);
- }
-
- ~SamplingOffsetCallback() { stopVsyncListener(); }
-
- SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
- SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
-
- void startVsyncListener() {
- std::lock_guard lock(mMutex);
- if (mVsyncListening) return;
-
- mPhaseIntervalSetting = Phase::ZERO;
- mVSyncSource->setVSyncEnabled(true);
- mVsyncListening = true;
- }
-
- void stopVsyncListener() {
- std::lock_guard lock(mMutex);
- stopVsyncListenerLocked();
- }
-
-private:
- void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
- if (!mVsyncListening) return;
-
- mVSyncSource->setVSyncEnabled(false);
- mVsyncListening = false;
- }
-
- void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
- nsecs_t /*deadlineTimestamp*/) final {
- std::unique_lock<decltype(mMutex)> lock(mMutex);
-
- if (mPhaseIntervalSetting == Phase::ZERO) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
- mPhaseIntervalSetting = Phase::SAMPLING;
- mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
- return;
- }
-
- if (mPhaseIntervalSetting == Phase::SAMPLING) {
- mPhaseIntervalSetting = Phase::ZERO;
- mVSyncSource->setDuration(0ns, 0ns);
- stopVsyncListenerLocked();
- lock.unlock();
- mRegionSamplingThread.notifySamplingOffset();
- return;
- }
- }
-
- RegionSamplingThread& mRegionSamplingThread;
- const std::chrono::nanoseconds mTargetSamplingWorkDuration;
- mutable std::mutex mMutex;
- enum class Phase {
- ZERO,
- SAMPLING
- } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
- = Phase::ZERO;
- bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
- std::unique_ptr<VSyncSource> mVSyncSource;
-};
-
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
- const TimingTunables& tunables)
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables)
: mFlinger(flinger),
- mScheduler(scheduler),
mTunables(tunables),
mIdleTimer(
"RegSampIdle",
std::chrono::duration_cast<std::chrono::milliseconds>(
mTunables.mSamplingTimerTimeout),
[] {}, [this] { checkForStaleLuma(); }),
- mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
- tunables.mSamplingDuration)),
- lastSampleTime(0ns) {
+ mLastSampleTime(0ns) {
mThread = std::thread([this]() { threadMain(); });
pthread_setname_np(mThread.native_handle(), "RegionSampling");
mIdleTimer.start();
}
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
- : RegionSamplingThread(flinger, scheduler,
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger)
+ : RegionSamplingThread(flinger,
TimingTunables{defaultRegionSamplingWorkDuration,
defaultRegionSamplingPeriod,
defaultRegionSamplingTimerTimeout}) {}
@@ -224,48 +147,46 @@
void RegionSamplingThread::checkForStaleLuma() {
std::lock_guard lock(mThreadControlMutex);
- if (mDiscardedFrames > 0) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
- mDiscardedFrames = 0;
- mPhaseCallback->startVsyncListener();
+ if (mSampleRequestTime.has_value()) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+ mSampleRequestTime.reset();
+ mFlinger.scheduleRegionSamplingThread();
}
}
-void RegionSamplingThread::notifyNewContent() {
- doSample();
+void RegionSamplingThread::onCompositionComplete(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
+ doSample(samplingDeadline);
}
-void RegionSamplingThread::notifySamplingOffset() {
- doSample();
-}
-
-void RegionSamplingThread::doSample() {
+void RegionSamplingThread::doSample(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
std::lock_guard lock(mThreadControlMutex);
- auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
- if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+ const auto now = std::chrono::steady_clock::now();
+ if (mLastSampleTime + mTunables.mSamplingPeriod > now) {
+ // content changed, but we sampled not too long ago, so we need to sample some time in the
+ // future.
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
- if (mDiscardedFrames == 0) mDiscardedFrames++;
+ mSampleRequestTime = now;
return;
}
- if (mDiscardedFrames < maxRegionSamplingSkips) {
+ if (!mSampleRequestTime.has_value() || now - *mSampleRequestTime < maxRegionSamplingDelay) {
// If there is relatively little time left for surfaceflinger
// until the next vsync deadline, defer this sampling work
// to a later frame, when hopefully there will be more time.
- const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime());
- if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
+ if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
- mDiscardedFrames++;
+ mSampleRequestTime = mSampleRequestTime.value_or(now);
return;
}
}
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
- mDiscardedFrames = 0;
- lastSampleTime = now;
+ mSampleRequestTime.reset();
+ mLastSampleTime = now;
mIdleTimer.reset();
- mPhaseCallback->stopVsyncListener();
mSampleRequested = true;
mCondition.notify_one();
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 86632db..2231853 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -63,9 +63,8 @@
struct EnvironmentTimingTunables : TimingTunables {
EnvironmentTimingTunables();
};
- explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
- const TimingTunables& tunables);
- explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger);
~RegionSamplingThread();
@@ -76,12 +75,11 @@
// Remove the listener to stop receiving median luma notifications.
void removeListener(const sp<IRegionSamplingListener>& listener);
- // Notifies sampling engine that new content is available. This will trigger a sampling
- // pass at some point in the future.
- void notifyNewContent();
-
- // Notifies the sampling engine that it has a good timing window in which to sample.
- void notifySamplingOffset();
+ // Notifies sampling engine that composition is done and new content is
+ // available, and the deadline for the sampling work on the main thread to
+ // be completed without eating the budget of another frame.
+ void onCompositionComplete(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
private:
struct Descriptor {
@@ -99,7 +97,7 @@
const sp<GraphicBuffer>& buffer, const Point& leftTop,
const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
- void doSample();
+ void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
void binderDied(const wp<IBinder>& who) override;
void checkForStaleLuma();
@@ -107,20 +105,18 @@
void threadMain();
SurfaceFlinger& mFlinger;
- Scheduler& mScheduler;
const TimingTunables mTunables;
scheduler::OneShotTimer mIdleTimer;
- std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
-
std::thread mThread;
std::mutex mThreadControlMutex;
std::condition_variable_any mCondition;
bool mRunning GUARDED_BY(mThreadControlMutex) = true;
bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
- uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0;
- std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
+ std::optional<std::chrono::steady_clock::time_point> mSampleRequestTime
+ GUARDED_BY(mThreadControlMutex);
+ std::chrono::steady_clock::time_point mLastSampleTime GUARDED_BY(mThreadControlMutex);
std::mutex mSamplingMutex;
std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index ce5c31a..50b38c9 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -60,8 +60,7 @@
mRegistration.schedule({.workDuration = mWorkDuration.count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = mLastCallTime.count()});
- LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
- "Error scheduling callback: rc %X", scheduleResult);
+ LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
}
void stop() {
@@ -100,8 +99,7 @@
mRegistration.schedule({.workDuration = mWorkDuration.count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = vsyncTime});
- LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
- "Error rescheduling callback: rc %X", scheduleResult);
+ LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
}
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index f4bc2a1..0563795 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,8 +84,11 @@
LayerHistory::~LayerHistory() = default;
void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
- auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), 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());
+ }
+ auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
mLayerInfos.emplace_back(layer, std::move(info));
}
@@ -94,7 +97,7 @@
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
[layer](const auto& pair) { return pair.first == layer; });
- LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, 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) {
@@ -111,7 +114,11 @@
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
[layer](const auto& pair) { return pair.first == layer; });
- LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+ if (it == mLayerInfos.end()) {
+ // Offscreen layer
+ ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
+ return;
+ }
const auto& info = it->second;
const auto layerProps = LayerInfo::LayerProps{
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index ba03c89..34cc389 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -272,7 +272,7 @@
// Used for sanitizing the heuristic data. If two frames are less than
// this period apart from each other they'll be considered as duplicates.
- static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs();
+ static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs();
// Used for sanitizing the heuristic data. If two frames are more than
// this period apart from each other, the interval between them won't be
// taken into account when calculating average frame rate.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 7ff0ddf..4d51125 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -23,7 +23,6 @@
#include <utils/threads.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include "EventThread.h"
#include "FrameTimeline.h"
@@ -33,27 +32,32 @@
namespace android::impl {
void MessageQueue::Handler::dispatchRefresh() {
- if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+ if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) {
mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
}
}
void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
- if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) {
mVsyncId = vsyncId;
mExpectedVSyncTime = expectedVSyncTimestamp;
mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
}
}
+bool MessageQueue::Handler::invalidatePending() {
+ constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh;
+ return (mEventMask.load() & pendingMask) != 0;
+}
+
void MessageQueue::Handler::handleMessage(const Message& message) {
switch (message.what) {
case INVALIDATE:
- android_atomic_and(~eventMaskInvalidate, &mEventMask);
+ mEventMask.fetch_and(~eventMaskInvalidate);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
case REFRESH:
- android_atomic_and(~eventMaskRefresh, &mEventMask);
+ mEventMask.fetch_and(~eventMaskRefresh);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
}
@@ -106,7 +110,7 @@
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
- mVsync.mScheduled = false;
+ mVsync.scheduled = false;
}
mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
{targetWakeupTime, readyTime, vsyncTime}),
@@ -131,9 +135,10 @@
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
- if (mVsync.mScheduled) {
- mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
- mVsync.lastCallbackTime.count()});
+ if (mVsync.scheduled) {
+ mVsync.expectedWakeupTime = mVsync.registration->schedule(
+ {mVsync.workDuration.get().count(),
+ /*readyDuration=*/0, mVsync.lastCallbackTime.count()});
}
}
@@ -176,10 +181,11 @@
}
std::lock_guard lock(mVsync.mutex);
- mVsync.mScheduled = true;
- mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
- .readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.count()});
+ mVsync.scheduled = true;
+ mVsync.expectedWakeupTime =
+ mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ .readyDuration = 0,
+ .earliestVsync = mVsync.lastCallbackTime.count()});
}
void MessageQueue::refresh() {
@@ -200,4 +206,19 @@
}
}
+std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() {
+ if (mHandler->invalidatePending()) {
+ return std::chrono::steady_clock::now();
+ }
+
+ std::lock_guard lock(mVsync.mutex);
+ if (mVsync.scheduled) {
+ LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled");
+ const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime);
+ return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime);
+ }
+
+ return std::nullopt;
+}
+
} // namespace android::impl
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 2934af0..58ce9b9 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -72,6 +72,7 @@
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void invalidate() = 0;
virtual void refresh() = 0;
+ virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0;
};
// ---------------------------------------------------------------------------
@@ -81,9 +82,13 @@
class MessageQueue : public android::MessageQueue {
protected:
class Handler : public MessageHandler {
- enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
+ enum : uint32_t {
+ eventMaskInvalidate = 0x1,
+ eventMaskRefresh = 0x2,
+ eventMaskTransaction = 0x4
+ };
MessageQueue& mQueue;
- int32_t mEventMask;
+ std::atomic<uint32_t> mEventMask;
std::atomic<int64_t> mVsyncId;
std::atomic<nsecs_t> mExpectedVSyncTime;
@@ -92,6 +97,7 @@
void handleMessage(const Message& message) override;
virtual void dispatchRefresh();
virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
+ virtual bool invalidatePending();
};
friend class Handler;
@@ -107,7 +113,8 @@
TracedOrdinal<std::chrono::nanoseconds> workDuration
GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
- bool mScheduled GUARDED_BY(mutex) = false;
+ bool scheduled GUARDED_BY(mutex) = false;
+ std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex);
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
@@ -141,6 +148,8 @@
// sends REFRESH message at next VSYNC
void refresh() override;
+
+ std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index ce3b0c6..16f041a 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -15,7 +15,6 @@
*/
#include "OneShotTimer.h"
-
#include <utils/Log.h>
#include <utils/Timers.h>
#include <chrono>
@@ -42,11 +41,14 @@
OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
const ResetCallback& resetCallback,
- const TimeoutCallback& timeoutCallback)
- : mName(std::move(name)),
+ const TimeoutCallback& timeoutCallback, std::unique_ptr<Clock> clock)
+ : mClock(std::move(clock)),
+ mName(std::move(name)),
mInterval(interval),
mResetCallback(resetCallback),
- mTimeoutCallback(timeoutCallback) {}
+ mTimeoutCallback(timeoutCallback) {
+ LOG_ALWAYS_FATAL_IF(!mClock, "Clock must not be provided");
+}
OneShotTimer::~OneShotTimer() {
stop();
@@ -112,7 +114,7 @@
break;
}
- auto triggerTime = std::chrono::steady_clock::now() + mInterval;
+ auto triggerTime = mClock->now() + mInterval;
state = TimerState::WAITING;
while (state == TimerState::WAITING) {
constexpr auto zero = std::chrono::steady_clock::duration::zero();
@@ -128,10 +130,9 @@
state = checkForResetAndStop(state);
if (state == TimerState::RESET) {
- triggerTime = std::chrono::steady_clock::now() + mInterval;
+ triggerTime = mClock->now() + mInterval;
state = TimerState::WAITING;
- } else if (state == TimerState::WAITING &&
- (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+ } else if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= zero) {
triggerTimeout = true;
state = TimerState::IDLE;
}
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 3690ce7..09265bb 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -20,6 +20,7 @@
#include <chrono>
#include <condition_variable>
#include <thread>
+#include "../Clock.h"
#include <android-base/thread_annotations.h>
@@ -37,7 +38,8 @@
using TimeoutCallback = std::function<void()>;
OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
- const TimeoutCallback& timeoutCallback);
+ const TimeoutCallback& timeoutCallback,
+ std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
~OneShotTimer();
// Initializes and turns on the idle timer.
@@ -78,6 +80,9 @@
// Thread waiting for timer to expire.
std::thread mThread;
+ // Clock object for the timer. Mocked in unit tests.
+ std::unique_ptr<Clock> mClock;
+
// Semaphore to keep mThread synchronized.
sem_t mSemaphore;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1d25c72..fac2c65 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -583,13 +583,9 @@
scheduler::LayerHistory::LayerVoteType voteType;
- if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+ if (!mOptions.useContentDetection ||
+ layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
- } else if (!mOptions.useContentDetection) {
- // If the content detection feature is off, all layers are registered at Max. We still keep
- // the layer history, since we use it for other features (like Frame Rate API), so layers
- // still need to be registered.
- voteType = scheduler::LayerHistory::LayerVoteType::Max;
} else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
voteType = scheduler::LayerHistory::LayerVoteType::Min;
@@ -597,6 +593,9 @@
voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
}
+ // 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);
}
@@ -927,4 +926,11 @@
}
}
+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());
+ return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4d1f3c6..49d3d93 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -149,6 +149,8 @@
bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const
EXCLUDES(mFrameRateOverridesMutex);
+ std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
+
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 9d71103..b52706f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,8 +16,10 @@
#pragma once
+#include <utils/Log.h>
#include <utils/Timers.h>
#include <functional>
+#include <optional>
#include <string>
#include "StrongTyping.h"
@@ -26,7 +28,8 @@
class TimeKeeper;
class VSyncTracker;
-enum class ScheduleResult { Scheduled, CannotSchedule, Error };
+using ScheduleResult = std::optional<nsecs_t>;
+
enum class CancelResult { Cancelled, TooLate, Error };
/*
@@ -121,11 +124,8 @@
*
* \param [in] token The callback to schedule.
* \param [in] scheduleTiming The timing information for this schedule call
- * \return A ScheduleResult::Scheduled if callback was scheduled.
- * A ScheduleResult::CannotSchedule
- * if (workDuration + readyDuration - earliestVsync) is in the past,
- * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
- * there was another error.
+ * \return The expected callback time if a callback was scheduled.
+ * std::nullopt if the callback is not registered.
*/
virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ca6ea27..28be962 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -26,6 +26,20 @@
namespace android::scheduler {
using base::StringAppendF;
+namespace {
+nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
+ const VSyncDispatch::ScheduleTiming& timing) {
+ return nextVsyncTime - timing.readyDuration - timing.workDuration;
+}
+
+nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
+ const VSyncDispatch::ScheduleTiming& timing) {
+ const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+ std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+ return getExpectedCallbackTime(nextVsyncTime, timing);
+}
+} // namespace
+
VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
TimeKeeper::~TimeKeeper() = default;
@@ -74,7 +88,7 @@
bool const wouldSkipAVsyncTarget =
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
if (wouldSkipAVsyncTarget) {
- return ScheduleResult::Scheduled;
+ return getExpectedCallbackTime(nextVsyncTime, timing);
}
bool const alreadyDispatchedForVsync = mLastDispatchTime &&
@@ -89,7 +103,7 @@
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
- return ScheduleResult::Scheduled;
+ return getExpectedCallbackTime(nextVsyncTime, timing);
}
void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
@@ -317,7 +331,7 @@
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
ScheduleTiming scheduleTiming) {
- auto result = ScheduleResult::Error;
+ ScheduleResult result;
{
std::lock_guard lock(mMutex);
@@ -333,11 +347,11 @@
auto const rearmImminent = now > mIntendedWakeupTime;
if (CC_UNLIKELY(rearmImminent)) {
callback->addPendingWorkloadUpdate(scheduleTiming);
- return ScheduleResult::Scheduled;
+ return getExpectedCallbackTime(mTracker, now, scheduleTiming);
}
result = callback->schedule(scheduleTiming, mTracker, now);
- if (result == ScheduleResult::CannotSchedule) {
+ if (!result.has_value()) {
return result;
}
@@ -416,7 +430,7 @@
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mValidToken) {
- return ScheduleResult::Error;
+ return std::nullopt;
}
return mDispatch.get().schedule(mToken, scheduleTiming);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5f5987f..0e222ab 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -23,7 +23,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceFlinger.h"
-#include "TraceUtils.h"
#include <android-base/properties.h>
#include <android/configuration.h>
@@ -58,13 +57,13 @@
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
+#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <private/gui/SyncFeatures.h>
#include <renderengine/RenderEngine.h>
-#include <statslog.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
@@ -288,6 +287,8 @@
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
const String16 sDump("android.permission.DUMP");
+const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
+
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
// ---------------------------------------------------------------------------
@@ -307,6 +308,7 @@
Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
bool SurfaceFlinger::useFrameRateApi;
+bool SurfaceFlinger::enableSdrDimming;
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
switch(displayColorSetting) {
@@ -382,6 +384,12 @@
mColorSpaceAgnosticDataspace =
static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
+ mLayerCachingEnabled = [] {
+ const bool enable =
+ android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+ return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+ }();
+
useContextPriority = use_context_priority(true);
using Values = SurfaceFlingerProperties::primary_display_orientation_values;
@@ -472,6 +480,9 @@
base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
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));
}
SurfaceFlinger::~SurfaceFlinger() = default;
@@ -627,7 +638,6 @@
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mFrameTracer->initialize();
- mTimeStats->onBootFinished();
mFrameTimeline->onBootFinished();
// wait patiently for the window manager death
@@ -1299,6 +1309,11 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+ *success = mTimeStats->onPullAtom(atomId, pulledData);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
@@ -1477,8 +1492,13 @@
}
return ftl::chain(schedule([=]() MAIN_THREAD {
- if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
- return getHwComposer().setDisplayBrightness(*displayId,
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ if (enableSdrDimming) {
+ display->getCompositionDisplay()
+ ->setDisplayBrightness(brightness.sdrWhitePointNits,
+ brightness.displayBrightnessNits);
+ }
+ return getHwComposer().setDisplayBrightness(display->getPhysicalId(),
brightness.displayBrightness);
} else {
ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
@@ -1700,11 +1720,11 @@
}
SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
- // We are storing the last 2 present fences. If sf's phase offset is to be
- // woken up before the actual vsync but targeting the next vsync, we need to check
- // fence N-2
- return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ const auto now = systemTime();
+ const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
+ const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
+ return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
}
bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1738,12 +1758,6 @@
}
void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
- const auto vsyncIn = [&] {
- if (!ATRACE_ENABLED()) return 0.f;
- return (expectedVSyncTime - systemTime()) / 1e6f;
- }();
-
- ATRACE_FORMAT("onMessageReceived %" PRId64 " vsyncIn %.2fms", vsyncId, vsyncIn);
switch (what) {
case MessageQueue::INVALIDATE: {
onMessageInvalidate(vsyncId, expectedVSyncTime);
@@ -1757,8 +1771,6 @@
}
void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
- ATRACE_CALL();
-
const nsecs_t frameStart = systemTime();
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
@@ -1773,6 +1785,13 @@
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
mScheduledPresentTime = expectedVSyncTime;
+ const auto vsyncIn = [&] {
+ if (!ATRACE_ENABLED()) return 0.f;
+ return (mExpectedPresentTime - systemTime()) / 1e6f;
+ }();
+ ATRACE_FORMAT("onMessageInvalidate %" PRId64 " vsyncIn %.2fms%s", vsyncId, vsyncIn,
+ mExpectedPresentTime == expectedVSyncTime ? "" : " (adjusted)");
+
// When Backpressure propagation is enabled we want to give a small grace period
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
@@ -1901,6 +1920,7 @@
mRefreshPending = true;
onMessageRefresh();
}
+ notifyRegionSamplingThread();
}
bool SurfaceFlinger::handleMessageTransaction() {
@@ -1969,6 +1989,8 @@
std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
}
+ refreshArgs.earliestPresentTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+
mGeometryInvalid = false;
// Store the present time just before calling to the composition engine so we could notify
@@ -2029,6 +2051,9 @@
ATRACE_CALL();
bool refreshNeeded = handlePageFlip();
+ // Send on commit callbacks
+ mTransactionCallbackInvoker.sendCallbacks();
+
if (mVisibleRegionsDirty) {
computeLayerBounds();
}
@@ -2183,20 +2208,10 @@
mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
if (layer->isVisible() &&
compositionDisplay->belongsInOutput(layer->getCompositionEngineLayerFE())) {
- bool isHdr = false;
- switch (layer->getDataSpace()) {
- case ui::Dataspace::BT2020:
- case ui::Dataspace::BT2020_HLG:
- case ui::Dataspace::BT2020_PQ:
- case ui::Dataspace::BT2020_ITU:
- case ui::Dataspace::BT2020_ITU_HLG:
- case ui::Dataspace::BT2020_ITU_PQ:
- isHdr = true;
- break;
- default:
- isHdr = false;
- break;
- }
+ const Dataspace transfer =
+ static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
+ const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
+ transfer == Dataspace::TRANSFER_HLG);
if (isHdr) {
info.numberOfHdrLayers++;
@@ -2298,10 +2313,6 @@
}
}
- if (mLumaSampling && mRegionSamplingThread) {
- mRegionSamplingThread->notifyNewContent();
- }
-
// Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
if (ATRACE_ENABLED()) {
@@ -2635,6 +2646,7 @@
builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
builder.setName(state.displayName);
const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+ compositionDisplay->setLayerCachingEnabled(mLayerCachingEnabled);
sp<compositionengine::DisplaySurface> displaySurface;
sp<IGraphicBufferProducer> producer;
@@ -2751,6 +2763,7 @@
(currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
currentState.orientedDisplaySpaceRect);
+ mDefaultDisplayTransformHint = display->getTransformHint();
}
if (currentState.width != drawingState.width ||
currentState.height != drawingState.height) {
@@ -2808,16 +2821,19 @@
}
void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
- /*
- * Traversal of the children
- * (perform the transaction for each of them if needed)
- */
+ // Commit display transactions
+ const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
+ if (displayTransactionNeeded) {
+ processDisplayChangesLocked();
+ processDisplayHotplugEventsLocked();
+ }
- if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
- mForceTraversal = false;
+ // Commit layer transactions. This needs to happen after display transactions are
+ // committed because some geometry logic relies on display orientation.
+ if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) {
mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
- if (!trFlags) return;
+ if (!trFlags && !displayTransactionNeeded) return;
const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion)
@@ -2829,15 +2845,7 @@
});
}
- /*
- * Perform display own transactions if needed
- */
-
- if (transactionFlags & eDisplayTransactionNeeded) {
- processDisplayChangesLocked();
- processDisplayHotplugEventsLocked();
- }
-
+ // Update transform hint
if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
// The transform hint might have changed for some layers
// (either because a display has changed, or because a layer
@@ -3057,8 +3065,7 @@
configs.late.sfWorkDuration);
mRegionSamplingThread =
- new RegionSamplingThread(*this, *mScheduler,
- RegionSamplingThread::EnvironmentTimingTunables());
+ new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
// Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
@@ -3096,7 +3103,7 @@
void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
- signalSynchronousTransactions();
+ signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
mAnimTransactionPending = false;
}
@@ -3257,70 +3264,38 @@
const sp<IBinder>& parentHandle,
const sp<Layer>& parentLayer, bool addToCurrentState,
uint32_t* outTransformHint) {
- // add this layer to the current state list
- {
- Mutex::Autolock _l(mStateLock);
- sp<Layer> parent;
- if (parentHandle != nullptr) {
- parent = fromHandleLocked(parentHandle).promote();
- if (parent == nullptr) {
- return NAME_NOT_FOUND;
- }
- } else {
- parent = parentLayer;
- }
-
- if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
- ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
- ISurfaceComposer::MAX_LAYERS);
- return NO_MEMORY;
- }
-
- mLayersByLocalBinderToken.emplace(handle->localBinder(), lbc);
-
- if (parent == nullptr && addToCurrentState) {
- mCurrentState.layersSortedByZ.add(lbc);
- } else if (parent == nullptr) {
- lbc->onRemovedFromCurrentState();
- } else if (parent->isRemovedFromCurrentState()) {
- parent->addChild(lbc);
- lbc->onRemovedFromCurrentState();
- } else {
- parent->addChild(lbc);
- }
-
- if (gbc != nullptr) {
- mGraphicBufferProducerList.insert(IInterface::asBinder(gbc).get());
- 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());
- }
- }
-
- if (const auto token = getInternalDisplayTokenLocked()) {
- const ssize_t index = mCurrentState.displays.indexOfKey(token);
- if (index >= 0) {
- const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
- lbc->updateTransformHint(ui::Transform::toRotationFlags(state.orientation));
- }
- }
- if (outTransformHint) {
- *outTransformHint = lbc->getTransformHint();
- }
-
- mLayersAdded = true;
+ 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);
+
+ // Create a transaction includes the initial parent and producer.
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+
+ ComposerState composerState;
+ composerState.state.what = layer_state_t::eLayerCreated;
+ composerState.state.surface = handle;
+ states.add(composerState);
+
+ lbc->updateTransformHint(mDefaultDisplayTransformHint);
+ if (outTransformHint) {
+ *outTransformHint = mDefaultDisplayTransformHint;
+ }
// attach this layer to the client
client->attachLayer(handle, lbc);
- return NO_ERROR;
+ 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) {
@@ -3550,7 +3525,9 @@
// Generate a CountDownLatch pending state if this is a synchronous transaction.
if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
- (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+ (state.inputWindowCommands.syncInputWindows
+ ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction)
+ : CountDownLatch::eSyncTransaction));
}
mTransactionQueue.emplace(state);
@@ -3575,10 +3552,10 @@
}
}
-void SurfaceFlinger::signalSynchronousTransactions() {
+void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) {
for (auto it = mTransactionCommittedSignals.begin();
it != mTransactionCommittedSignals.end();) {
- if ((*it)->countDown() == 0) {
+ if ((*it)->countDown(flag)) {
it = mTransactionCommittedSignals.erase(it);
} else {
it++;
@@ -3789,19 +3766,47 @@
uint32_t SurfaceFlinger::setClientStateLocked(
const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
+ std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& outListenerCallbacks) {
const layer_state_t& s = composerState.state;
const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
+
+ std::vector<ListenerCallbacks> filteredListeners;
for (auto& listener : s.listeners) {
+ // Starts a registration but separates the callback ids according to callback type. This
+ // allows the callback invoker to send on latch callbacks earlier.
// note that startRegistration will not re-register if the listener has
// already be registered for a prior surface control
- mTransactionCallbackInvoker.startRegistration(listener);
- listenerCallbacks.insert(listener);
+
+ ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
+ if (!onCommitCallbacks.callbackIds.empty()) {
+ mTransactionCallbackInvoker.startRegistration(onCommitCallbacks);
+ filteredListeners.push_back(onCommitCallbacks);
+ outListenerCallbacks.insert(onCommitCallbacks);
+ }
+
+ ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
+ if (!onCompleteCallbacks.callbackIds.empty()) {
+ mTransactionCallbackInvoker.startRegistration(onCompleteCallbacks);
+ filteredListeners.push_back(onCompleteCallbacks);
+ outListenerCallbacks.insert(onCompleteCallbacks);
+ }
}
+ const uint64_t what = s.what;
+ uint32_t flags = 0;
sp<Layer> layer = nullptr;
if (s.surface) {
- layer = fromHandleLocked(s.surface).promote();
+ if (what & layer_state_t::eLayerCreated) {
+ layer = handleLayerCreatedLocked(s.surface, privileged);
+ if (layer) {
+ // put the created layer into mLayersByLocalBinderToken.
+ mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer);
+ flags |= eTransactionNeeded | eTraversalNeeded;
+ mLayersAdded = true;
+ }
+ } else {
+ layer = fromHandleLocked(s.surface).promote();
+ }
} else {
// The client may provide us a null handle. Treat it as if the layer was removed.
ALOGW("Attempt to set client state with a null layer handle");
@@ -3814,10 +3819,6 @@
return 0;
}
- uint32_t flags = 0;
-
- const uint64_t what = s.what;
-
// Only set by BLAST adapter layers
if (what & layer_state_t::eProducerDisconnect) {
layer->onDisconnect();
@@ -4032,6 +4033,16 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eBufferCropChanged) {
+ if (layer->setBufferCrop(s.bufferCrop)) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ if (what & layer_state_t::eDestinationFrameChanged) {
+ if (layer->setDestinationFrame(s.destinationFrame)) {
+ flags |= eTraversalNeeded;
+ }
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
@@ -4049,8 +4060,8 @@
}
}
std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
- for (auto& [listener, callbackIds] : s.listeners) {
+ if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+ for (auto& [listener, callbackIds] : filteredListeners) {
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
@@ -4271,14 +4282,7 @@
sp<Layer>* outLayer) {
LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
args.textureName = getNewTexture();
- sp<BufferStateLayer> layer;
- {
- // TODO (b/173538294): Investigate why we need mStateLock here and above in
- // createBufferQueue layer. Is it the renderengine::Image?
- Mutex::Autolock lock(mStateLock);
- layer = getFactory().createBufferStateLayer(args);
-
- }
+ sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
*handle = layer->getHandle();
*outLayer = layer;
@@ -4366,7 +4370,7 @@
setPowerModeInternal(display, hal::PowerMode::ON);
const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-
+ mDefaultDisplayTransformHint = display->getTransformHint();
// Use phase of 0 since phase is not known.
// Use latency of 0, which will snap to the ideal latency.
DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
@@ -4988,6 +4992,13 @@
result.append("\n");
}
+ {
+ DumpArgs plannerArgs;
+ plannerArgs.add(); // first argument is ignored
+ plannerArgs.add(String16("--layers"));
+ dumpPlannerInfo(plannerArgs, result);
+ }
+
/*
* Dump HWComposer state
*/
@@ -5120,17 +5131,15 @@
// captureLayers and captureDisplay will handle the permission check in the function
case CAPTURE_LAYERS:
case CAPTURE_DISPLAY:
- case SET_DISPLAY_BRIGHTNESS:
case SET_FRAME_TIMELINE_INFO:
case GET_GPU_CONTEXT_PRIORITY:
case GET_EXTRA_BUFFER_COUNT: {
// This is not sensitive information, so should not require permission control.
return OK;
}
+ case SET_DISPLAY_BRIGHTNESS:
case ADD_HDR_LAYER_INFO_LISTENER:
case REMOVE_HDR_LAYER_INFO_LISTENER: {
- // TODO (b/183985553): Should getting & setting brightness be part of this...?
- // codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
@@ -5165,6 +5174,13 @@
}
return PERMISSION_DENIED;
}
+ case ON_PULL_ATOM: {
+ const int uid = IPCThreadState::self()->getCallingUid();
+ if (uid == AID_SYSTEM) {
+ return OK;
+ }
+ return PERMISSION_DENIED;
+ }
}
// These codes are used for the IBinder protocol to either interrogate the recipient
@@ -5175,9 +5191,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1038 are currently used for backdoors. The code
+ // Numbers from 1000 to 1040 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 <= 1039) {
+ if (code >= 1000 && code <= 1040) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5574,6 +5590,45 @@
mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
return NO_ERROR;
}
+ // Toggle caching feature
+ // First argument is an int32 - nonzero enables caching and zero disables caching
+ // 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) {
+ const auto token = getPhysicalDisplayToken(
+ static_cast<PhysicalDisplayId>(inputDisplayId));
+ if (!token) {
+ ALOGE("No display with id: %" PRIu64, inputDisplayId);
+ return NAME_NOT_FOUND;
+ }
+
+ inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId);
+ }
+ {
+ Mutex::Autolock lock(mStateLock);
+ mLayerCachingEnabled = n != 0;
+ for (const auto& [_, display] : mDisplays) {
+ if (!inputId || *inputId == display->getPhysicalId()) {
+ display->enableLayerCaching(mLayerCachingEnabled);
+ }
+ }
+ }
+ return OK;
+ }).get();
+
+ if (error != OK) {
+ return error;
+ }
+ invalidateHwcGeometry();
+ repaintEverything();
+ return NO_ERROR;
+ }
}
}
return err;
@@ -5669,6 +5724,14 @@
}
}
+static bool hasCaptureBlackoutContentPermission() {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+ PermissionCache::checkPermission(sCaptureBlackoutContent, pid, uid);
+}
+
static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -5839,6 +5902,10 @@
Rect layerStackSpaceRect;
ui::Dataspace dataspace;
bool captureSecureLayers;
+
+ // Call this before holding mStateLock to avoid any deadlocking.
+ bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
+
{
Mutex::Autolock lock(mStateLock);
@@ -5848,9 +5915,8 @@
return NAME_NOT_FOUND;
}
- const int uid = IPCThreadState::self()->getCallingUid();
- const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
- if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
+ if (!canCaptureBlackoutContent &&
+ parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
return PERMISSION_DENIED;
}
@@ -6000,8 +6066,7 @@
return BAD_VALUE;
}
- const int uid = IPCThreadState::self()->getCallingUid();
- const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
+ bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
if (mRefreshPending) {
@@ -6021,8 +6086,9 @@
status_t result = NO_ERROR;
renderArea->render([&] {
- result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem,
- regionSampling, grayscale, captureResults);
+ result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+ canCaptureBlackoutContent, regionSampling, grayscale,
+ captureResults);
});
captureResults.result = result;
@@ -6034,8 +6100,9 @@
status_t SurfaceFlinger::renderScreenImplLocked(
const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
- bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
+ ScreenCaptureResults& captureResults) {
ATRACE_CALL();
traverseLayers([&](Layer* layer) {
@@ -6048,7 +6115,7 @@
// We allow the system server to take screenshots of secure layers for
// use in situations like the Screen-rotation animation and place
// the impetus on WindowManager to not persist them.
- if (captureResults.capturedSecureLayers && !forSystem) {
+ if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
ALOGW("FB is protected: PERMISSION_DENIED");
return PERMISSION_DENIED;
}
@@ -6164,7 +6231,7 @@
void SurfaceFlinger::setInputWindowsFinished() {
Mutex::Autolock _l(mStateLock);
- signalSynchronousTransactions();
+ signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
}
// ---------------------------------------------------------------------------
@@ -6388,13 +6455,17 @@
void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
mNumLayers++;
- mScheduler->registerLayer(layer);
+ if (!layer->isRemovedFromCurrentState()) {
+ mScheduler->registerLayer(layer);
+ }
}
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
- mScheduler->deregisterLayer(layer);
mNumLayers--;
removeFromOffscreenLayers(layer);
+ if (!layer->isRemovedFromCurrentState()) {
+ mScheduler->deregisterLayer(layer);
+ }
}
// WARNING: ONLY CALL THIS FROM LAYER DTOR
@@ -6622,6 +6693,100 @@
}
}
+void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
+ const wp<IBinder>& parent, const wp<Layer> parentLayer,
+ const wp<IBinder>& producer) {
+ Mutex::Autolock lock(mCreatedLayersLock);
+ mCreatedLayers[handle->localBinder()] =
+ std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer);
+}
+
+auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
+ Mutex::Autolock lock(mCreatedLayersLock);
+ BBinder* b = nullptr;
+ if (handle) {
+ b = handle->localBinder();
+ }
+
+ if (b == nullptr) {
+ return std::unique_ptr<LayerCreatedState>(nullptr);
+ }
+
+ auto it = mCreatedLayers.find(b);
+ if (it == mCreatedLayers.end()) {
+ ALOGE("Can't find layer from handle %p", handle.get());
+ return std::unique_ptr<LayerCreatedState>(nullptr);
+ }
+
+ auto state = std::move(it->second);
+ mCreatedLayers.erase(it);
+ return state;
+}
+
+sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged) {
+ const auto& state = getLayerCreatedState(handle);
+ if (!state) {
+ return nullptr;
+ }
+
+ sp<Layer> layer = state->layer.promote();
+ if (!layer) {
+ ALOGE("Invalid layer %p", state->layer.unsafe_get());
+ return nullptr;
+ }
+
+ sp<Layer> parent;
+ bool allowAddRoot = privileged;
+ if (state->initialParent != nullptr) {
+ parent = fromHandleLocked(state->initialParent.promote()).promote();
+ if (parent == nullptr) {
+ ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
+ allowAddRoot = false;
+ }
+ } else if (state->initialParentLayer != nullptr) {
+ parent = state->initialParentLayer.promote();
+ allowAddRoot = false;
+ }
+
+ if (parent == nullptr && allowAddRoot) {
+ mCurrentState.layersSortedByZ.add(layer);
+ } else if (parent == nullptr) {
+ layer->onRemovedFromCurrentState();
+ } else if (parent->isRemovedFromCurrentState()) {
+ parent->addChild(layer);
+ layer->onRemovedFromCurrentState();
+ } else {
+ parent->addChild(layer);
+ }
+
+ 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());
+ }
+ }
+
+ return layer;
+}
+
+void SurfaceFlinger::scheduleRegionSamplingThread() {
+ static_cast<void>(schedule([&] { notifyRegionSamplingThread(); }));
+}
+
+void SurfaceFlinger::notifyRegionSamplingThread() {
+ if (!mLumaSampling || !mRegionSamplingThread) {
+ return;
+ }
+
+ mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 893b3d8..cb8d312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -271,6 +271,10 @@
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;
+
// must be called before clients can connect
void init() ANDROID_API;
@@ -467,24 +471,31 @@
class CountDownLatch {
public:
- explicit CountDownLatch(int32_t count) : mCount(count) {}
+ enum {
+ eSyncTransaction = 1 << 0,
+ eSyncInputWindows = 1 << 1,
+ };
+ explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
- int32_t countDown() {
+ // True if there is no waiting condition after count down.
+ bool countDown(uint32_t flag) {
std::unique_lock<std::mutex> lock(mMutex);
- if (mCount == 0) {
- return 0;
+ if (mFlags == 0) {
+ return true;
}
- if (--mCount == 0) {
+ mFlags &= ~flag;
+ if (mFlags == 0) {
mCountDownComplete.notify_all();
+ return true;
}
- return mCount;
+ return false;
}
// Return true if triggered.
bool wait_until(const std::chrono::seconds& timeout) const {
std::unique_lock<std::mutex> lock(mMutex);
const auto untilTime = std::chrono::system_clock::now() + timeout;
- while (mCount != 0) {
+ while (mFlags != 0) {
// Conditional variables can be woken up sporadically, so we check count
// to verify the wakeup was triggered by |countDown|.
if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
@@ -495,7 +506,7 @@
}
private:
- int32_t mCount;
+ uint32_t mFlags;
mutable std::condition_variable mCountDownComplete;
mutable std::mutex mMutex;
};
@@ -637,6 +648,7 @@
status_t getAnimationFrameStats(FrameStats* outStats) const override;
status_t overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes) override;
+ status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) override;
status_t enableVSyncInjections(bool enable) override;
status_t injectVSync(nsecs_t when) override;
status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
@@ -903,8 +915,8 @@
const sp<IScreenCaptureListener>&);
status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
const std::shared_ptr<renderengine::ExternalTexture>&,
- bool forSystem, bool regionSampling, bool grayscale,
- ScreenCaptureResults&);
+ bool canCaptureBlackoutContent, bool regionSampling,
+ bool grayscale, ScreenCaptureResults&);
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
@@ -1123,7 +1135,7 @@
// Add transaction to the Transaction Queue
void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
- void signalSynchronousTransactions();
+ void signalSynchronousTransactions(const uint32_t flag);
/*
* Generic Layer Metadata
@@ -1228,6 +1240,7 @@
int mDebugRegion = 0;
bool mDebugDisableHWC = false;
bool mDebugDisableTransformHint = false;
+ bool mLayerCachingEnabled = false;
volatile nsecs_t mDebugInTransaction = 0;
bool mForceFullDamage = false;
bool mPropagateBackpressureClientComposition = false;
@@ -1391,6 +1404,38 @@
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
+ mutable Mutex mCreatedLayersLock;
+ struct LayerCreatedState {
+ LayerCreatedState(const wp<Layer>& layer, const wp<IBinder>& parent,
+ const wp<Layer> parentLayer, const wp<IBinder>& producer)
+ : layer(layer),
+ initialParent(parent),
+ initialParentLayer(parentLayer),
+ initialProducer(producer) {}
+ 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.
+ wp<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;
+ };
+
+ // A temporay pool that store the created layers and will be added to current state in main
+ // thread.
+ std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
+ void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
+ const wp<IBinder>& parent, const wp<Layer> parentLayer,
+ const wp<IBinder>& producer);
+ auto getLayerCreatedState(const sp<IBinder>& handle);
+ sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged)
+ REQUIRES(mStateLock);
+
+ std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
+
+ void scheduleRegionSamplingThread();
+ void notifyRegionSamplingThread();
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index b3dca78..4a69c8f 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -321,6 +321,10 @@
return defaultValue;
}
+bool enable_sdr_dimming(bool defaultValue) {
+ return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
+}
+
int32_t display_update_imminent_timeout_ms(int32_t defaultValue) {
auto temp = SurfaceFlingerProperties::display_update_imminent_timeout_ms();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index b19d216..039d316 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -102,6 +102,8 @@
bool enable_layer_caching(bool defaultValue);
+bool enable_sdr_dimming(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 113f463..c5f1598 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -130,8 +130,8 @@
transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
const int32_t layerId(getLayerId(layer));
- addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(),
- layer->mCurrentState.active_legacy.transform.ty());
+ addPositionLocked(transaction, layerId, layer->mCurrentState.transform.tx(),
+ layer->mCurrentState.transform.ty());
addDepthLocked(transaction, layerId, layer->mCurrentState.z);
addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a);
addTransparentRegionLocked(transaction, layerId,
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 62fddb4..bcc3e4e 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -18,20 +18,13 @@
"libcutils",
"liblog",
"libprotobuf-cpp-lite",
- "libprotoutil",
- "libstatslog",
- "libstatspull",
- "libstatssocket",
+ "libtimestats_atoms_proto",
"libtimestats_proto",
"libui",
"libutils",
],
export_include_dirs: ["."],
export_shared_lib_headers: [
- "libprotoutil",
- "libstatslog",
- "libstatspull",
- "libstatssocket",
"libtimestats_proto",
],
cppflags: [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 2094972..d6a0787 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -19,11 +19,9 @@
#define LOG_TAG "TimeStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "TimeStats.h"
-
#include <android-base/stringprintf.h>
-#include <android/util/ProtoOutputStream.h>
#include <log/log.h>
+#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -31,147 +29,102 @@
#include <algorithm>
#include <chrono>
+#include "TimeStats.h"
#include "timestatsproto/TimeStatsHelper.h"
namespace android {
namespace impl {
-AStatsManager_PullAtomCallbackReturn TimeStats::pullAtomCallback(int32_t atom_tag,
- AStatsEventList* data,
- void* cookie) {
- impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
- AStatsManager_PullAtomCallbackReturn result = AStatsManager_PULL_SKIP;
- if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
- result = timeStats->populateGlobalAtom(data);
- } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
- result = timeStats->populateLayerAtom(data);
- }
-
- // Enable timestats now. The first full pull for a given build is expected to
- // have empty or very little stats, as stats are first enabled after the
- // first pull is completed for either the global or layer stats.
- timeStats->enable();
- return result;
-}
-
namespace {
-// Histograms align with the order of fields in SurfaceflingerStatsLayerInfo.
-const std::array<std::string, 6> kHistogramNames = {
- "present2present", "post2present", "acquire2present",
- "latch2present", "desired2present", "post2acquire",
-};
-std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t>& histogram,
- size_t maxPulledHistogramBuckets) {
+FrameTimingHistogram histogramToProto(const std::unordered_map<int32_t, int32_t>& histogram,
+ size_t maxPulledHistogramBuckets) {
auto buckets = std::vector<std::pair<int32_t, int32_t>>(histogram.begin(), histogram.end());
std::sort(buckets.begin(), buckets.end(),
[](std::pair<int32_t, int32_t>& left, std::pair<int32_t, int32_t>& right) {
return left.second > right.second;
});
- util::ProtoOutputStream proto;
+ FrameTimingHistogram histogramProto;
int histogramSize = 0;
for (const auto& bucket : buckets) {
if (++histogramSize > maxPulledHistogramBuckets) {
break;
}
- proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
- 1 /* field id */,
- (int32_t)bucket.first);
- proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
- 2 /* field id */,
- (int64_t)bucket.second);
+ histogramProto.add_time_millis_buckets((int32_t)bucket.first);
+ histogramProto.add_frame_counts((int64_t)bucket.second);
}
-
- std::string byteString;
- proto.serializeToString(&byteString);
- return byteString;
+ return histogramProto;
}
-std::string frameRateVoteToProtoByteString(
- float refreshRate,
- TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
- TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
- util::ProtoOutputStream proto;
- proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
- proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
- static_cast<int>(frameRateCompatibility));
- proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
+SurfaceflingerStatsLayerInfo_SetFrameRateVote frameRateVoteToProto(
+ const TimeStats::SetFrameRateVote& setFrameRateVote) {
+ using FrameRateCompatibilityEnum =
+ SurfaceflingerStatsLayerInfo::SetFrameRateVote::FrameRateCompatibility;
+ using SeamlessnessEnum = SurfaceflingerStatsLayerInfo::SetFrameRateVote::Seamlessness;
- std::string byteString;
- proto.serializeToString(&byteString);
- return byteString;
+ SurfaceflingerStatsLayerInfo_SetFrameRateVote proto;
+ proto.set_frame_rate(setFrameRateVote.frameRate);
+ proto.set_frame_rate_compatibility(
+ static_cast<FrameRateCompatibilityEnum>(setFrameRateVote.frameRateCompatibility));
+ proto.set_seamlessness(static_cast<SeamlessnessEnum>(setFrameRateVote.seamlessness));
+ return proto;
}
} // namespace
-AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) {
+bool TimeStats::populateGlobalAtom(std::string* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
if (mTimeStats.statsStartLegacy == 0) {
- return AStatsManager_PULL_SKIP;
+ return false;
}
flushPowerTimeLocked();
-
+ SurfaceflingerStatsGlobalInfoWrapper atomList;
for (const auto& globalSlice : mTimeStats.stats) {
- AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
- mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFramesLegacy);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFramesLegacy);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFramesLegacy);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTimeLegacy);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresentLegacy.totalTime());
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCountLegacy);
- std::string frameDurationBytes =
- histogramToProtoByteString(mTimeStats.frameDurationLegacy.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(),
- frameDurationBytes.size());
- std::string renderEngineTimingBytes =
- histogramToProtoByteString(mTimeStats.renderEngineTimingLegacy.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event,
- (const uint8_t*)renderEngineTimingBytes.c_str(),
- renderEngineTimingBytes.size());
-
- mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalFrames);
- mStatsDelegate->statsEventWriteInt32(event,
- globalSlice.second.jankPayload.totalJankyFrames);
- mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongCpu);
- mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongGpu);
- mStatsDelegate->statsEventWriteInt32(event,
- globalSlice.second.jankPayload.totalSFUnattributed);
- mStatsDelegate->statsEventWriteInt32(event,
- globalSlice.second.jankPayload.totalAppUnattributed);
- mStatsDelegate->statsEventWriteInt32(event,
- globalSlice.second.jankPayload.totalSFScheduling);
- mStatsDelegate->statsEventWriteInt32(event,
- globalSlice.second.jankPayload.totalSFPredictionError);
- mStatsDelegate->statsEventWriteInt32(event,
- globalSlice.second.jankPayload.totalAppBufferStuffing);
- mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.displayRefreshRateBucket);
- std::string sfDeadlineMissedBytes =
- histogramToProtoByteString(globalSlice.second.displayDeadlineDeltas.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event,
- (const uint8_t*)sfDeadlineMissedBytes.c_str(),
- sfDeadlineMissedBytes.size());
- std::string sfPredictionErrorBytes =
- histogramToProtoByteString(globalSlice.second.displayPresentDeltas.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event,
- (const uint8_t*)sfPredictionErrorBytes.c_str(),
- sfPredictionErrorBytes.size());
- mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.renderRateBucket);
- mStatsDelegate->statsEventBuild(event);
+ SurfaceflingerStatsGlobalInfo* atom = atomList.add_atom();
+ atom->set_total_frames(mTimeStats.totalFramesLegacy);
+ atom->set_missed_frames(mTimeStats.missedFramesLegacy);
+ atom->set_client_composition_frames(mTimeStats.clientCompositionFramesLegacy);
+ atom->set_display_on_millis(mTimeStats.displayOnTimeLegacy);
+ atom->set_animation_millis(mTimeStats.presentToPresentLegacy.totalTime());
+ atom->set_event_connection_count(mTimeStats.displayEventConnectionsCountLegacy);
+ *atom->mutable_frame_duration() =
+ histogramToProto(mTimeStats.frameDurationLegacy.hist, mMaxPulledHistogramBuckets);
+ *atom->mutable_render_engine_timing() =
+ histogramToProto(mTimeStats.renderEngineTimingLegacy.hist,
+ mMaxPulledHistogramBuckets);
+ atom->set_total_timeline_frames(globalSlice.second.jankPayload.totalFrames);
+ atom->set_total_janky_frames(globalSlice.second.jankPayload.totalJankyFrames);
+ atom->set_total_janky_frames_with_long_cpu(globalSlice.second.jankPayload.totalSFLongCpu);
+ atom->set_total_janky_frames_with_long_gpu(globalSlice.second.jankPayload.totalSFLongGpu);
+ atom->set_total_janky_frames_sf_unattributed(
+ globalSlice.second.jankPayload.totalSFUnattributed);
+ atom->set_total_janky_frames_app_unattributed(
+ globalSlice.second.jankPayload.totalAppUnattributed);
+ atom->set_total_janky_frames_sf_scheduling(
+ globalSlice.second.jankPayload.totalSFScheduling);
+ atom->set_total_jank_frames_sf_prediction_error(
+ globalSlice.second.jankPayload.totalSFPredictionError);
+ atom->set_total_jank_frames_app_buffer_stuffing(
+ globalSlice.second.jankPayload.totalAppBufferStuffing);
+ atom->set_display_refresh_rate_bucket(globalSlice.first.displayRefreshRateBucket);
+ *atom->mutable_sf_deadline_misses() =
+ histogramToProto(globalSlice.second.displayDeadlineDeltas.hist,
+ mMaxPulledHistogramBuckets);
+ *atom->mutable_sf_prediction_errors() =
+ histogramToProto(globalSlice.second.displayPresentDeltas.hist,
+ mMaxPulledHistogramBuckets);
+ atom->set_render_rate_bucket(globalSlice.first.renderRateBucket);
}
+ // Always clear data.
clearGlobalLocked();
- return AStatsManager_PULL_SUCCESS;
+ return atomList.SerializeToString(pulledData);
}
-AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
+bool TimeStats::populateLayerAtom(std::string* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -198,69 +151,73 @@
dumpStats.resize(mMaxPulledLayers);
}
+ SurfaceflingerStatsLayerInfoWrapper atomList;
for (auto& layer : dumpStats) {
- AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
- mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
- mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
- mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames);
- mStatsDelegate->statsEventWriteInt64(event, layer->droppedFrames);
-
- for (const auto& name : kHistogramNames) {
- const auto& histogram = layer->deltas.find(name);
- if (histogram == layer->deltas.cend()) {
- mStatsDelegate->statsEventWriteByteArray(event, nullptr, 0);
- } else {
- std::string bytes = histogramToProtoByteString(histogram->second.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)bytes.c_str(),
- bytes.size());
- }
+ SurfaceflingerStatsLayerInfo* atom = atomList.add_atom();
+ atom->set_layer_name(layer->layerName);
+ atom->set_total_frames(layer->totalFrames);
+ atom->set_dropped_frames(layer->droppedFrames);
+ const auto& present2PresentHist = layer->deltas.find("present2present");
+ if (present2PresentHist != layer->deltas.cend()) {
+ *atom->mutable_present_to_present() =
+ histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& post2presentHist = layer->deltas.find("post2present");
+ if (post2presentHist != layer->deltas.cend()) {
+ *atom->mutable_post_to_present() =
+ histogramToProto(post2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& acquire2presentHist = layer->deltas.find("acquire2present");
+ if (acquire2presentHist != layer->deltas.cend()) {
+ *atom->mutable_acquire_to_present() =
+ histogramToProto(acquire2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& latch2presentHist = layer->deltas.find("latch2present");
+ if (latch2presentHist != layer->deltas.cend()) {
+ *atom->mutable_latch_to_present() =
+ histogramToProto(latch2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& desired2presentHist = layer->deltas.find("desired2present");
+ if (desired2presentHist != layer->deltas.cend()) {
+ *atom->mutable_desired_to_present() =
+ histogramToProto(desired2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& post2acquireHist = layer->deltas.find("post2acquire");
+ if (post2acquireHist != layer->deltas.cend()) {
+ *atom->mutable_post_to_acquire() =
+ histogramToProto(post2acquireHist->second.hist, mMaxPulledHistogramBuckets);
}
- mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
- mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
- mStatsDelegate->statsEventWriteInt32(event, layer->uid);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFScheduling);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFPredictionError);
- mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppBufferStuffing);
- mStatsDelegate->statsEventWriteInt32(
- event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket
- mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket
- std::string frameRateVoteBytes =
- frameRateVoteToProtoByteString(layer->setFrameRateVote.frameRate,
- layer->setFrameRateVote.frameRateCompatibility,
- layer->setFrameRateVote.seamlessness);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
- frameRateVoteBytes.size()); // set_frame_rate_vote
- std::string appDeadlineMissedBytes =
- histogramToProtoByteString(layer->deltas["appDeadlineDeltas"].hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event,
- (const uint8_t*)appDeadlineMissedBytes.c_str(),
- appDeadlineMissedBytes.size());
-
- mStatsDelegate->statsEventBuild(event);
+ atom->set_late_acquire_frames(layer->lateAcquireFrames);
+ atom->set_bad_desired_present_frames(layer->badDesiredPresentFrames);
+ atom->set_uid(layer->uid);
+ atom->set_total_timeline_frames(layer->jankPayload.totalFrames);
+ atom->set_total_janky_frames(layer->jankPayload.totalJankyFrames);
+ atom->set_total_janky_frames_with_long_cpu(layer->jankPayload.totalSFLongCpu);
+ atom->set_total_janky_frames_with_long_gpu(layer->jankPayload.totalSFLongGpu);
+ atom->set_total_janky_frames_sf_unattributed(layer->jankPayload.totalSFUnattributed);
+ atom->set_total_janky_frames_app_unattributed(layer->jankPayload.totalAppUnattributed);
+ atom->set_total_janky_frames_sf_scheduling(layer->jankPayload.totalSFScheduling);
+ atom->set_total_jank_frames_sf_prediction_error(layer->jankPayload.totalSFPredictionError);
+ atom->set_total_jank_frames_app_buffer_stuffing(layer->jankPayload.totalAppBufferStuffing);
+ atom->set_display_refresh_rate_bucket(layer->displayRefreshRateBucket);
+ atom->set_render_rate_bucket(layer->renderRateBucket);
+ *atom->mutable_set_frame_rate_vote() = frameRateVoteToProto(layer->setFrameRateVote);
+ *atom->mutable_app_deadline_misses() =
+ histogramToProto(layer->deltas["appDeadlineDeltas"].hist,
+ mMaxPulledHistogramBuckets);
}
+
+ // Always clear data.
clearLayersLocked();
- return AStatsManager_PULL_SUCCESS;
+ return atomList.SerializeToString(pulledData);
}
-TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {}
+TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
-TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
- std::optional<size_t> maxPulledLayers,
+TimeStats::TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets) {
- if (statsDelegate != nullptr) {
- mStatsDelegate = std::move(statsDelegate);
- }
-
if (maxPulledLayers) {
mMaxPulledLayers = *maxPulledLayers;
}
@@ -270,18 +227,19 @@
}
}
-TimeStats::~TimeStats() {
- std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
-}
+bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+ bool success = false;
+ if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
+ success = populateGlobalAtom(pulledData);
+ } else if (atomId == 10063) { // SURFACEFLINGER_STATS_LAYER_INFO
+ success = populateLayerAtom(pulledData);
+ }
-void TimeStats::onBootFinished() {
- std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- nullptr, TimeStats::pullAtomCallback, this);
- mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- nullptr, TimeStats::pullAtomCallback, this);
+ // Enable timestats now. The first full pull for a given build is expected to
+ // have empty or very little stats, as stats are first enabled after the
+ // first pull is completed for either the global or layer stats.
+ enable();
+ return success;
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
@@ -393,12 +351,16 @@
std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
}
+static int32_t toMs(nsecs_t nanos) {
+ int64_t millis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos))
+ .count();
+ millis = std::clamp(millis, int64_t(INT32_MIN), int64_t(INT32_MAX));
+ return static_cast<int32_t>(millis);
+}
+
static int32_t msBetween(nsecs_t start, nsecs_t end) {
- int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::nanoseconds(end - start))
- .count();
- delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
- return static_cast<int32_t>(delta);
+ return toMs(end - start);
}
void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
@@ -469,8 +431,8 @@
return true;
}
-static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) {
- return (fps.getIntValue() / bucketWidth) * bucketWidth;
+static int32_t clampToNearestBucket(Fps fps, size_t bucketWidth) {
+ return std::round(fps.getValue() / bucketWidth) * bucketWidth;
}
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
@@ -483,10 +445,10 @@
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
const int32_t refreshRateBucket =
- clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
const int32_t renderRateBucket =
- clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate,
- RENDER_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(renderRate ? *renderRate : displayRefreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
while (!timeRecords.empty()) {
if (!recordReadyLocked(layerId, &timeRecords[0])) break;
ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
@@ -787,7 +749,7 @@
static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
JankType::SurfaceFlingerCpuDeadlineMissed | JankType::SurfaceFlingerGpuDeadlineMissed |
JankType::AppDeadlineMissed | JankType::PredictionError |
- JankType::SurfaceFlingerScheduling | JankType::BufferStuffing;
+ JankType::SurfaceFlingerScheduling;
template <class T>
static void updateJankPayload(T& t, int32_t reasons) {
@@ -813,9 +775,11 @@
if ((reasons & JankType::SurfaceFlingerScheduling) != 0) {
t.jankPayload.totalSFScheduling++;
}
- if ((reasons & JankType::BufferStuffing) != 0) {
- t.jankPayload.totalAppBufferStuffing++;
- }
+ }
+
+ // We want to track BufferStuffing separately as it can provide info on latency issues
+ if (reasons & JankType::BufferStuffing) {
+ t.jankPayload.totalAppBufferStuffing++;
}
}
@@ -839,10 +803,10 @@
static const std::string kDefaultLayerName = "none";
const int32_t refreshRateBucket =
- clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
const int32_t renderRateBucket =
- clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
- RENDER_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
if (!mTimeStats.stats.count(timelineKey)) {
@@ -869,10 +833,9 @@
// TimeStats Histograms only retain positive values, so we don't need to check if these
// deadlines were really missed if we know that the frame had jank, since deadlines
// that were met will be dropped.
- timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta));
- timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter));
- timeStatsLayer.deltas["appDeadlineDeltas"].insert(
- static_cast<int32_t>(info.appDeadlineDelta));
+ timelineStats.displayDeadlineDeltas.insert(toMs(info.displayDeadlineDelta));
+ timelineStats.displayPresentDeltas.insert(toMs(info.displayPresentJitter));
+ timeStatsLayer.deltas["appDeadlineDeltas"].insert(toMs(info.appDeadlineDelta));
}
}
@@ -1061,6 +1024,7 @@
void TimeStats::clearAll() {
std::lock_guard<std::mutex> lock(mMutex);
+ mTimeStats.stats.clear();
clearGlobalLocked();
clearLayersLocked();
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index a87b7cb..5b0f5bd 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -29,9 +29,6 @@
#include <../Fps.h>
#include <gui/JankInfo.h>
-#include <stats_event.h>
-#include <stats_pull_atom_callback.h>
-#include <statslog.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
@@ -54,9 +51,8 @@
virtual ~TimeStats() = default;
- // Called once boot has been finished to perform additional capabilities,
- // e.g. registration to statsd.
- virtual void onBootFinished() = 0;
+ // Process a pull request from statsd.
+ virtual bool onPullAtom(const int atomId, std::string* pulledData);
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
@@ -232,58 +228,11 @@
public:
TimeStats();
-
- // Delegate to the statsd service and associated APIs.
- // Production code may use this class directly, whereas unit test may define
- // a subclass for ease of testing.
- class StatsEventDelegate {
- public:
- virtual ~StatsEventDelegate() = default;
- virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
- return AStatsEventList_addStatsEvent(data);
- }
- virtual void setStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomMetadata* metadata,
- AStatsManager_PullAtomCallback callback,
- void* cookie) {
- return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
- }
-
- virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
- return AStatsManager_clearPullAtomCallback(atom_tag);
- }
-
- virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
- return AStatsEvent_setAtomId(event, atom_id);
- }
-
- virtual void statsEventWriteInt32(AStatsEvent* event, int32_t field) {
- return AStatsEvent_writeInt32(event, field);
- }
-
- virtual void statsEventWriteInt64(AStatsEvent* event, int64_t field) {
- return AStatsEvent_writeInt64(event, field);
- }
-
- virtual void statsEventWriteString8(AStatsEvent* event, const char* field) {
- return AStatsEvent_writeString(event, field);
- }
-
- virtual void statsEventWriteByteArray(AStatsEvent* event, const uint8_t* buf,
- size_t numBytes) {
- return AStatsEvent_writeByteArray(event, buf, numBytes);
- }
-
- virtual void statsEventBuild(AStatsEvent* event) { return AStatsEvent_build(event); }
- };
// For testing only for injecting custom dependencies.
- TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
- std::optional<size_t> maxPulledLayers,
+ TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
- ~TimeStats() override;
-
- void onBootFinished() override;
+ bool onPullAtom(const int atomId, std::string* pulledData) override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -332,11 +281,8 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag,
- AStatsEventList* data,
- void* cookie);
- AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data);
- AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
+ bool populateGlobalAtom(std::string* pulledData);
+ bool populateLayerAtom(std::string* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate,
@@ -366,7 +312,6 @@
static const size_t RENDER_RATE_BUCKET_WIDTH = REFRESH_RATE_BUCKET_WIDTH;
static const size_t MAX_NUM_LAYER_STATS = 200;
static const size_t MAX_NUM_PULLED_LAYERS = MAX_NUM_LAYER_STATS;
- std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
size_t mMaxPulledLayers = MAX_NUM_PULLED_LAYERS;
size_t mMaxPulledHistogramBuckets = 6;
};
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
new file mode 100644
index 0000000..0cf086f
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
@@ -0,0 +1,36 @@
+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 {
+ name: "libtimestats_atoms_proto",
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "timestats_atoms.proto",
+ ],
+
+ proto: {
+ type: "lite",
+ export_proto_headers: true,
+ },
+
+ cppflags: [
+ "-Werror",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-disabled-macro-expansion",
+ "-Wno-float-conversion",
+ "-Wno-float-equal",
+ "-Wno-format",
+ "-Wno-old-style-cast",
+ "-Wno-padded",
+ "-Wno-sign-conversion",
+ "-Wno-undef",
+ "-Wno-unused-parameter",
+ ],
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h b/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h
new file mode 100644
index 0000000..d305cb4
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h
@@ -0,0 +1,23 @@
+/*
+ * 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 is used here to disable the warnings emitted from the protobuf
+// headers. By adding #pragma before including layer.pb.h, it suppresses
+// protobuf warnings, but allows the rest of the files to continuing using
+// the current flags.
+// This file should be included instead of directly including timestats_atoms.b.h
+#pragma GCC system_header
+#include <timestats_atoms.pb.h>
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
new file mode 100644
index 0000000..133a541
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
+
+// This is a copy of surfaceflinger's atoms from frameworks/proto_logging/stats/atoms.proto.
+// Pulled atoms for surfaceflinger must be routed through system server since surfaceflinger is
+// in the bootstrap namespace. This copy is used to pass the atoms as protos to system server using
+// proto lite to serialize/deserialize the atoms.
+
+// These wrappers are so that we can pass a List<Atom> as a single byte string.
+// They are not in atoms.proto
+message SurfaceflingerStatsGlobalInfoWrapper {
+ repeated SurfaceflingerStatsGlobalInfo atom = 1;
+}
+
+message SurfaceflingerStatsLayerInfoWrapper {
+ repeated SurfaceflingerStatsLayerInfo atom = 1;
+}
+
+/**
+ * Global display pipeline metrics reported by SurfaceFlinger.
+ * Metrics exist beginning in Android 11.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsGlobalInfo {
+ // Aggregated refresh rate buckets that layers were presenting at. Buckets
+ // are defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimenstion in collecting per-refresh rate
+ // jank statistics.
+ optional int32 display_refresh_rate_bucket = 18;
+ // Aggregated render rate buckets that layers were overridden to run at.
+ // Buckets are defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimension in collecting per-render rate
+ // jank statistics.
+ optional int32 render_rate_bucket = 21;
+ // Total number of frames presented during the tracing period
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 total_frames = 1;
+ // Total number of frames missed
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 missed_frames = 2;
+ // Total number of frames that fell back to client composition
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 client_composition_frames = 3;
+ // Total time the display was turned on
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 display_on_millis = 4;
+ // Total time that was spent performing animations.
+ // This is derived from the present-to-present layer histogram.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 animation_millis = 5;
+ // Total number of event connections tracked by SurfaceFlinger at the time
+ // of this pull. If this number grows prohibitively large, then this can
+ // cause jank due to resource contention.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int32 event_connection_count = 6;
+ // Set of timings measured from when SurfaceFlinger began compositing a
+ // frame, until the frame was requested to be presented to the display. This
+ // measures SurfaceFlinger's total CPU walltime on the critical path per
+ // frame.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional FrameTimingHistogram frame_duration = 7;
+ // Set of timings measured from when SurfaceFlinger first began using the
+ // GPU to composite a frame, until the GPU has finished compositing that
+ // frame. This measures the total additional time SurfaceFlinger needed to
+ // perform due to falling back into GPU composition.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional FrameTimingHistogram render_engine_timing = 8;
+ // Number of frames where SF saw a frame, based on its frame timeline.
+ // Frame timelines may include transactions without updating buffer contents.
+ // Introduced in Android 12.
+ optional int32 total_timeline_frames = 9;
+ // Number of frames where SF saw a janky frame.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames = 10;
+ // Number of janky frames where SF spent a long time on the CPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_cpu = 11;
+ // Number of janky frames where SF spent a long time on the GPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_gpu = 12;
+ // Number of janky frames where SF missed the frame deadline, but there
+ // was not an attributed reason (e.g., maybe HWC missed?)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_unattributed = 13;
+ // Number of janky frames where the app missed the frame deadline, but
+ // there was not an attributed reason
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_app_unattributed = 14;
+ // Number of janky frames that were caused because of scheduling errors in
+ // SF that resulted in early present (e.g., SF sending a buffer to the
+ // composition engine earlier than expected, resulting in a present that is
+ // one vsync early)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_scheduling = 15;
+ // Number of frames that were classified as jank because of possible drift in
+ // vsync predictions.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_sf_prediction_error = 16;
+ // Number of janky frames where the app was in a buffer stuffed state (more
+ // than one buffer ready to be presented at the same vsync). Usually caused
+ // when the first frame is unusually long, the following frames enter into a
+ // stuffed state.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_app_buffer_stuffing = 17;
+ // Buckets of timings in ms by which SurfaceFlinger's deadline was missed
+ // while latching and presenting frames.
+ // Introduced in Android 12.
+ optional FrameTimingHistogram sf_deadline_misses = 19;
+ // Buckets of timings in ms by which the Vsync prediction drifted, when
+ // compared to the actual hardware vsync.
+ // Introduced in Android 12.
+ optional FrameTimingHistogram sf_prediction_errors = 20;
+
+ // Next ID: 22
+}
+
+/**
+ * Per-layer display pipeline metrics reported by SurfaceFlinger.
+ * Metrics exist beginning in Android 11.
+ * The number of layers uploaded may be restricted due to size limitations.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsLayerInfo {
+ // UID of the application who submitted this layer for presentation
+ // This is intended to be used as a dimension for surfacing rendering
+ // statistics to applications.
+ // Introduced in Android 12.
+ optional int32 uid = 12;
+ // Refresh rate bucket that the layer was presenting at. Buckets are
+ // defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimension in collecting per-refresh rate
+ // jank statistics
+ optional int32 display_refresh_rate_bucket = 22;
+ // Render rate bucket that the layer was submitting frames at. Buckets are
+ // defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimension in collecting per-render rate
+ // jank statistics.
+ optional int32 render_rate_bucket = 23;
+ // The layer for this set of metrics
+ // In many scenarios the package name is included in the layer name, e.g.,
+ // layers created by Window Manager. But this is not a guarantee - in the
+ // general case layer names are arbitrary debug names.
+ optional string layer_name = 1;
+ // Total number of frames presented
+ optional int64 total_frames = 2;
+ // Total number of dropped frames while latching a buffer for this layer.
+ optional int64 dropped_frames = 3;
+ // Set of timings measured between successive presentation timestamps.
+ optional FrameTimingHistogram present_to_present = 4;
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was actually presented to the
+ // display.
+ optional FrameTimingHistogram post_to_present = 5;
+ // Set of timings measured from when a buffer is ready to be presented,
+ // until the buffer was actually presented to the display.
+ optional FrameTimingHistogram acquire_to_present = 6;
+ // Set of timings measured from when a buffer was latched by
+ // SurfaceFlinger, until the buffer was presented to the display
+ optional FrameTimingHistogram latch_to_present = 7;
+ // Set of timings measured from the desired presentation to the actual
+ // presentation time
+ optional FrameTimingHistogram desired_to_present = 8;
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was ready to be presented.
+ optional FrameTimingHistogram post_to_acquire = 9;
+ // Frames missed latch because the acquire fence didn't fire
+ optional int64 late_acquire_frames = 10;
+ // Frames latched early because the desired present time was bad
+ optional int64 bad_desired_present_frames = 11;
+ // Number of frames where SF saw a frame, based on its frame timeline.
+ // Frame timelines may include transactions without updating buffer contents.
+ // Introduced in Android 12.
+ optional int32 total_timeline_frames = 13;
+ // Number of frames where SF saw a janky frame.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames = 14;
+ // Number of janky frames where SF spent a long time on the CPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_cpu = 15;
+ // Number of janky frames where SF spent a long time on the GPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_gpu = 16;
+ // Number of janky frames where SF missed the frame deadline, but there
+ // was not an attributed reason (e.g., maybe HWC missed?)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_unattributed = 17;
+ // Number of janky frames where the app missed the frame deadline, but
+ // there was not an attributed reason
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_app_unattributed = 18;
+ // Number of janky frames that were caused because of scheduling errors in
+ // SF that resulted in early present (e.g., SF sending a buffer to the
+ // composition engine earlier than expected, resulting in a present that is
+ // one vsync early)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_scheduling = 19;
+ // Number of frames that were classified as jank because of possible drift in
+ // vsync predictions.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_sf_prediction_error = 20;
+ // Number of janky frames where the app was in a buffer stuffed state (more
+ // than one buffer ready to be presented at the same vsync). Usually caused
+ // when the first frame is unusually long, the following frames enter into a
+ // stuffed state.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_app_buffer_stuffing = 21;
+
+ /**
+ * Encapsulates the FrameRateVote information sent by the application while
+ * calling setFrameRate.
+ * Metrics exist beginning in Android 12.
+ */
+ message SetFrameRateVote {
+ // The desired frame rate the application wishes to run on.
+ optional float frame_rate = 1;
+
+ enum FrameRateCompatibility {
+ FRAME_RATE_UNDEFINED = 0;
+ FRAME_RATE_DEFAULT = 1;
+ FRAME_RATE_EXACT_OR_MULTIPLE = 2;
+ }
+
+ // Specifies how to interpret the frame rate associated with the layer.
+ // Defined in Layer.h
+ optional FrameRateCompatibility frame_rate_compatibility = 2;
+
+ enum Seamlessness {
+ SEAMLESS_UNDEFINED = 0;
+ SEAMLESS_SHOULD_BE_SEAMLESS = 1;
+ SEAMLESS_NOT_REQUIRED = 2;
+ }
+ // Indicates whether seamless refresh rate switch is required or not.
+ optional Seamlessness seamlessness = 3;
+ }
+
+ // The last frame rate vote set by the application.
+ // Introduced in Android 12.
+ optional SetFrameRateVote set_frame_rate_vote = 24;
+ // Buckets of timings in ms by which the app deadline was missed while
+ // submitting work for a frame.
+ // Introduced in Android 12.
+ optional FrameTimingHistogram app_deadline_misses = 25;
+
+ // Next ID: 26
+}
+
+/**
+ * Histogram of frame counts bucketed by time in milliseconds.
+ * Because of size limitations, we hard-cap the number of buckets, with
+ * buckets for corresponding to larger milliseconds being less precise.
+ */
+message FrameTimingHistogram {
+ // Timings in milliseconds that describes a set of histogram buckets
+ repeated int32 time_millis_buckets = 1;
+ // Number of frames that match to each time_millis, i.e. the bucket
+ // contents
+ // It's required that len(time_millis) == len(frame_count)
+ repeated int64 frame_counts = 2;
+}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 3590e76..4f4c02b 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -36,13 +36,17 @@
// <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
// >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
//
-// See CallbackIdsHash for a explaniation of why this works
+// See CallbackIdsHash for a explanation of why this works
static int compareCallbackIds(const std::vector<CallbackId>& c1,
const std::vector<CallbackId>& c2) {
if (c1.empty()) {
return !c2.empty();
}
- return c1.front() - c2.front();
+ return c1.front().id - c2.front().id;
+}
+
+static bool containsOnCommitCallbacks(const std::vector<CallbackId>& callbacks) {
+ return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
}
TransactionCallbackInvoker::~TransactionCallbackInvoker() {
@@ -114,39 +118,69 @@
return NO_ERROR;
}
+status_t TransactionCallbackInvoker::finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) {
+ auto listener = mPendingTransactions.find(handle->listener);
+ if (listener != mPendingTransactions.end()) {
+ auto& pendingCallbacks = listener->second;
+ auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+ if (pendingCallback != pendingCallbacks.end()) {
+ auto& pendingCount = pendingCallback->second;
+
+ // Decrease the pending count for this listener
+ if (--pendingCount == 0) {
+ pendingCallbacks.erase(pendingCallback);
+ }
+ } else {
+ ALOGW("there are more latched callbacks than there were registered callbacks");
+ }
+ if (listener->second.size() == 0) {
+ mPendingTransactions.erase(listener);
+ }
+ } else {
+ ALOGW("cannot find listener in mPendingTransactions");
+ }
+
+ status_t err = addCallbackHandle(handle, jankData);
+ if (err != NO_ERROR) {
+ ALOGE("could not add callback handle");
+ return err;
+ }
+ return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles(
+ const std::deque<sp<CallbackHandle>>& handles,
+ std::deque<sp<CallbackHandle>>& outRemainingHandles) {
+ if (handles.empty()) {
+ return NO_ERROR;
+ }
+ std::lock_guard lock(mMutex);
+ const std::vector<JankData>& jankData = std::vector<JankData>();
+ for (const auto& handle : handles) {
+ if (!containsOnCommitCallbacks(handle->callbackIds)) {
+ outRemainingHandles.push_back(handle);
+ continue;
+ }
+ status_t err = finalizeCallbackHandle(handle, jankData);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
status_t TransactionCallbackInvoker::finalizePendingCallbackHandles(
const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
if (handles.empty()) {
return NO_ERROR;
}
std::lock_guard lock(mMutex);
-
for (const auto& handle : handles) {
- auto listener = mPendingTransactions.find(handle->listener);
- if (listener != mPendingTransactions.end()) {
- auto& pendingCallbacks = listener->second;
- auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
- if (pendingCallback != pendingCallbacks.end()) {
- auto& pendingCount = pendingCallback->second;
-
- // Decrease the pending count for this listener
- if (--pendingCount == 0) {
- pendingCallbacks.erase(pendingCallback);
- }
- } else {
- ALOGW("there are more latched callbacks than there were registered callbacks");
- }
- if (listener->second.size() == 0) {
- mPendingTransactions.erase(listener);
- }
- } else {
- ALOGW("cannot find listener in mPendingTransactions");
- }
-
- status_t err = addCallbackHandle(handle, jankData);
+ status_t err = finalizeCallbackHandle(handle, jankData);
if (err != NO_ERROR) {
- ALOGE("could not add callback handle");
return err;
}
}
@@ -243,7 +277,8 @@
}
// If the transaction has been latched
- if (transactionStats.latchTime >= 0) {
+ if (transactionStats.latchTime >= 0 &&
+ !containsOnCommitCallbacks(transactionStats.callbackIds)) {
if (!mPresentFence) {
break;
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index caa8a4f..184b151 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -74,6 +74,8 @@
// Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented.
status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
const std::vector<JankData>& jankData);
+ status_t finalizeOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+ std::deque<sp<CallbackHandle>>& outRemainingHandles);
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
@@ -95,6 +97,9 @@
status_t addCallbackHandle(const sp<CallbackHandle>& handle,
const std::vector<JankData>& jankData) REQUIRES(mMutex);
+ status_t finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) REQUIRES(mMutex);
+
class CallbackDeathRecipient : public IBinder::DeathRecipient {
public:
// This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index ee5542d..78f8a2f 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -463,3 +463,12 @@
access: Readonly
prop_name: "ro.surface_flinger.enable_layer_caching"
}
+
+# Enables SDR layer dimming
+prop {
+ api_name: "enable_sdr_dimming"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.enable_sdr_dimming"
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 47e14f6..9c567d6 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -53,6 +53,10 @@
prop_name: "ro.surface_flinger.protected_contents"
}
prop {
+ api_name: "enable_sdr_dimming"
+ prop_name: "ro.surface_flinger.enable_sdr_dimming"
+ }
+ prop {
api_name: "force_hwc_copy_for_virtual_displays"
prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
}
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 58b039e..9cf7c09 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -52,12 +52,15 @@
}
};
-TEST_F(InvalidHandleTest, createSurfaceInvalidHandle) {
- auto notSc = makeNotSurfaceControl();
- ASSERT_EQ(nullptr,
- mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
- notSc->getHandle())
- .get());
+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) {
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 011ff70..965aac3 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -767,7 +767,8 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
}
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
@@ -938,7 +939,8 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
}
-TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 7581cd3..c8eeac6 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1336,7 +1336,8 @@
Color::GREEN, true /* filtered */);
}
-TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+// TODO (b/186543004): Fix & re-enable
+TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) {
sp<SurfaceControl> layer;
Transaction transaction;
ASSERT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 2828d61..43d957c 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -273,6 +273,198 @@
}
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferRotationTransform) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> parent;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = LayerTransactionTest::createLayer("parent", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const uint32_t bufferWidth = 1500;
+ const uint32_t bufferHeight = 300;
+
+ const uint32_t layerWidth = 300;
+ const uint32_t layerHeight = 1500;
+
+ const uint32_t testArea = 4;
+ const float cornerRadius = 120.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, bufferWidth, bufferHeight));
+
+ Transaction()
+ .reparent(layer, parent)
+ .setColor(parent, half3(0, 1, 0))
+ .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+ .setCornerRadius(parent, cornerRadius)
+
+ .setTransform(layer, ui::Transform::ROT_90)
+ .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+ .apply();
+ {
+ auto shot = getScreenCapture();
+ // Corners are transparent
+ // top-left
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ // top-right
+ shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+ // bottom-left
+ shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+ // bottom-right
+ shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+ layerHeight),
+ Color::BLACK);
+
+ // Area after corner radius is solid
+ // top-left to top-right under the corner
+ shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED);
+ // bottom-left to bottom-right above the corner
+ shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth,
+ layerHeight - cornerRadius),
+ Color::RED);
+ // left side after the corner
+ shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED);
+ // right side before the corner
+ shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius,
+ layerHeight),
+ Color::RED);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferCropTransform) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> parent;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = LayerTransactionTest::createLayer("parent", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const uint32_t bufferWidth = 150 * 2;
+ const uint32_t bufferHeight = 750 * 2;
+
+ const Rect bufferCrop(0, 0, 150, 750);
+
+ const uint32_t layerWidth = 300;
+ const uint32_t layerHeight = 1500;
+
+ const uint32_t testArea = 4;
+ const float cornerRadius = 120.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerQuadrant(layer, bufferWidth, bufferHeight, Color::RED,
+ Color::BLACK, Color::GREEN, Color::BLUE));
+
+ Transaction()
+ .reparent(layer, parent)
+ .setColor(parent, half3(0, 1, 0))
+ .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+ .setCornerRadius(parent, cornerRadius)
+
+ .setBufferCrop(layer, bufferCrop)
+ .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+ .apply();
+ {
+ auto shot = getScreenCapture();
+ // Corners are transparent
+ // top-left
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ // top-right
+ shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+ // bottom-left
+ shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+ // bottom-right
+ shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+ layerHeight),
+ Color::BLACK);
+
+ // Area after corner radius is solid
+ // since the buffer is scaled, there will blending so adjust some of the bounds when
+ // checking.
+ float adjustedCornerRadius = cornerRadius + 15;
+ float adjustedLayerHeight = layerHeight - 15;
+ float adjustedLayerWidth = layerWidth - 15;
+
+ // top-left to top-right under the corner
+ shot->expectColor(Rect(15, adjustedCornerRadius, adjustedLayerWidth,
+ adjustedCornerRadius + testArea),
+ Color::RED);
+ // bottom-left to bottom-right above the corner
+ shot->expectColor(Rect(15, adjustedLayerHeight - adjustedCornerRadius - testArea,
+ adjustedLayerWidth, adjustedLayerHeight - adjustedCornerRadius),
+ Color::RED);
+ // left side after the corner
+ shot->expectColor(Rect(adjustedCornerRadius, 15, adjustedCornerRadius + testArea,
+ adjustedLayerHeight),
+ Color::RED);
+ // right side before the corner
+ shot->expectColor(Rect(adjustedLayerWidth - adjustedCornerRadius - testArea, 15,
+ adjustedLayerWidth - adjustedCornerRadius, adjustedLayerHeight),
+ Color::RED);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildBufferRotationTransform) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> parent;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = LayerTransactionTest::createLayer("parent", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const uint32_t bufferWidth = 1500;
+ const uint32_t bufferHeight = 300;
+
+ const uint32_t layerWidth = 300;
+ const uint32_t layerHeight = 1500;
+
+ const uint32_t testArea = 4;
+ const float cornerRadius = 120.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::BLUE, bufferWidth, bufferHeight));
+
+ sp<SurfaceControl> child;
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::RED, bufferWidth, bufferHeight));
+
+ Transaction()
+ .reparent(layer, parent)
+ .reparent(child, layer)
+ .setColor(parent, half3(0, 1, 0))
+ .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+ .setCornerRadius(parent, cornerRadius) /* */
+
+ .setTransform(layer, ui::Transform::ROT_90)
+ .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+
+ .setTransform(child, ui::Transform::ROT_90)
+ .setDestinationFrame(child, Rect(0, 0, layerWidth, layerHeight))
+ .apply();
+ {
+ auto shot = getScreenCapture();
+ // Corners are transparent
+ // top-left
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ // top-right
+ shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+ // bottom-left
+ shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+ // bottom-right
+ shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+ layerHeight),
+ Color::BLACK);
+
+ // Area after corner radius is solid
+ // top-left to top-right under the corner
+ shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED);
+ // bottom-left to bottom-right above the corner
+ shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth,
+ layerHeight - cornerRadius),
+ Color::RED);
+ // left side after the corner
+ shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED);
+ // right side before the corner
+ shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius,
+ layerHeight),
+ Color::RED);
+ }
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
if (!deviceSupportsBlurs()) GTEST_SKIP();
if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 4753362..34c9182 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -19,6 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#include <gui/BufferItemConsumer.h>
+#include <private/android_filesystem_config.h>
#include "TransactionTestHarnesses.h"
namespace android {
@@ -170,7 +171,11 @@
args.displayToken = mDisplay;
ScreenCaptureResults captureResults;
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+ {
+ // Ensure the UID is not root because root has all permissions
+ UIDFaker f(AID_APP_START);
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+ }
Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 2e9c10c..6912fcf 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -84,7 +84,11 @@
Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ {
+ // Ensure the UID is not root because root has all permissions
+ UIDFaker f(AID_APP_START);
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ }
UIDFaker f(AID_SYSTEM);
@@ -528,7 +532,7 @@
ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}
-TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
+TEST_F(ScreenCaptureTest, CaptureSecureLayer) {
sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
ISurfaceComposerClient::eFXSurfaceBufferState);
sp<SurfaceControl> secureLayer =
@@ -552,8 +556,12 @@
args.childrenOnly = false;
ScreenCaptureResults captureResults;
- // Call from outside system with secure layers will result in permission denied
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+ {
+ // Ensure the UID is not root because root has all permissions
+ UIDFaker f(AID_APP_START);
+ // Call from outside system with secure layers will result in permission denied
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+ }
UIDFaker f(AID_SYSTEM);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 4ac096b..b33434e 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -94,6 +94,7 @@
"VSyncReactorTest.cpp",
"VsyncConfigurationTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
+ "mock/DisplayHardware/MockHWC2.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
@@ -127,6 +128,7 @@
"librenderengine",
"libserviceutils",
"libtimestats",
+ "libtimestats_atoms_proto",
"libtimestats_proto",
"libtrace_proto",
"perfetto_trace_protos",
@@ -152,14 +154,10 @@
"libnativewindow",
"libprocessgroup",
"libprotobuf-cpp-lite",
- "libprotoutil",
- "libstatslog",
- "libstatssocket",
"libSurfaceFlingerProp",
"libsync",
"libui",
"libutils",
- "libstatspull",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 54f4c7c..a9ad249 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -66,7 +66,7 @@
ALOGD("schedule: %zu", token.value());
if (mCallbacks.count(token) == 0) {
ALOGD("schedule: callback %zu not registered", token.value());
- return scheduler::ScheduleResult::Error;
+ return scheduler::ScheduleResult{};
}
auto& callback = mCallbacks.at(token);
@@ -75,7 +75,7 @@
callback.targetWakeupTime =
timing.earliestVsync - timing.workDuration - timing.readyDuration;
ALOGD("schedule: callback %zu scheduled", token.value());
- return scheduler::ScheduleResult::Scheduled;
+ return scheduler::ScheduleResult{callback.targetWakeupTime};
});
ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index a2291b2..dec0ff5 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -28,6 +28,7 @@
#include "FpsReporter.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
+#include "fake/FakeClock.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockEventThread.h"
#include "mock/MockFrameTimeline.h"
@@ -91,7 +92,9 @@
sp<Layer> mUnrelated;
sp<TestableFpsListener> mFpsListener;
- sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline, *(mFlinger.flinger()));
+ fake::FakeClock* mClock = new fake::FakeClock();
+ sp<FpsReporter> mFpsReporter =
+ new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock));
};
FpsReporterTest::FpsReporterTest() {
@@ -178,6 +181,7 @@
.WillOnce(Return(expectedFps));
mFpsReporter->addListener(mFpsListener, kTaskId);
+ mClock->advanceTime(600ms);
mFpsReporter->dispatchLayerFps();
EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
mFpsReporter->removeListener(mFpsListener);
@@ -187,5 +191,34 @@
mFpsReporter->dispatchLayerFps();
}
+TEST_F(FpsReporterTest, rateLimits) {
+ const constexpr int32_t kTaskId = 12;
+ LayerMetadata targetMetadata;
+ targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+ mTarget = createBufferStateLayer(targetMetadata);
+ mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+
+ float firstFps = 44.0;
+ float secondFps = 53.0;
+
+ EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence())))
+ .WillOnce(Return(firstFps))
+ .WillOnce(Return(secondFps));
+
+ mFpsReporter->addListener(mFpsListener, kTaskId);
+ mClock->advanceTime(600ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(secondFps, mFpsListener->lastReportedFps);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 7727052..c6a4115 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -73,7 +73,7 @@
mTokenManager = &mFrameTimeline->mTokenManager;
mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
- maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
+ maxTokens = mTokenManager->kMaxTokens;
}
// Each tracing session can be used for a single block of Start -> Stop.
@@ -111,9 +111,11 @@
mFrameTimeline->setSfPresent(2500, presentFence1);
}
- void flushTokens(nsecs_t flushTime) {
- std::lock_guard<std::mutex> lock(mTokenManager->mMutex);
- mTokenManager->flushTokens(flushTime);
+ void flushTokens() {
+ for (size_t i = 0; i < maxTokens; i++) {
+ mTokenManager->generateTokenForPredictions({});
+ }
+ EXPECT_EQ(getPredictions().size(), maxTokens);
}
SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
@@ -132,7 +134,7 @@
a.presentTime == b.presentTime;
}
- const std::map<int64_t, TokenManagerPrediction>& getPredictions() const {
+ const std::map<int64_t, TimelineItem>& getPredictions() const {
return mTokenManager->mPredictions;
}
@@ -155,7 +157,7 @@
TraceCookieCounter* mTraceCookieCounter;
FenceToFenceTimeMap fenceFactory;
uint32_t* maxDisplayFrames;
- nsecs_t maxTokenRetentionTime;
+ size_t maxTokens;
static constexpr pid_t kSurfaceFlingerPid = 666;
static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count();
static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count();
@@ -177,12 +179,11 @@
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
EXPECT_EQ(getPredictions().size(), 1u);
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30});
std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1);
// token1 should have expired
- EXPECT_EQ(getPredictions().size(), 1u);
EXPECT_EQ(predictions.has_value(), false);
predictions = mTokenManager->getPredictionsForToken(token2);
@@ -212,7 +213,7 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
@@ -707,7 +708,7 @@
sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
// Trigger a prediction expiry
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1065,7 +1066,7 @@
tracingSession->StartBlocking();
int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
// Set up the display frame
mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
@@ -1283,7 +1284,7 @@
mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
@@ -1347,6 +1348,81 @@
validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
}
+TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTracedProperly) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ constexpr nsecs_t appStartTime = std::chrono::nanoseconds(10ms).count();
+ constexpr nsecs_t appEndTime = std::chrono::nanoseconds(20ms).count();
+ constexpr nsecs_t appPresentTime = std::chrono::nanoseconds(30ms).count();
+ int64_t surfaceFrameToken =
+ mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
+
+ // Flush the token so that it would expire
+ flushTokens();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
+ sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+
+ constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(22ms).count();
+ constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count();
+ constexpr nsecs_t sfPresentTime = std::chrono::nanoseconds(30ms).count();
+ int64_t displayFrameToken =
+ mTokenManager->generateTokenForPredictions({sfStartTime, sfEndTime, sfPresentTime});
+
+ // First 2 cookies will be used by the DisplayFrame
+ int64_t traceCookie = snoopCurrentTraceCookie() + 2;
+
+ auto protoActualSurfaceFrameStart =
+ createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
+ displayFrameToken, sPidOne, sLayerNameOne,
+ FrameTimelineEvent::PRESENT_DROPPED, false, false,
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_EXPIRED);
+ auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setDropTime(sfStartTime);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(sfEndTime, presentFence1);
+ presentFence1->signalForTest(sfPresentTime);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // Display Frame 4 packets + SurfaceFrame 2 packets
+ ASSERT_EQ(packets.size(), 6u);
+
+ // Packet - 4 : ActualSurfaceFrameStart
+ const auto& packet4 = packets[4];
+ ASSERT_TRUE(packet4.has_timestamp());
+ EXPECT_EQ(packet4.timestamp(),
+ static_cast<uint64_t>(sfStartTime - SurfaceFrame::kPredictionExpiredStartTimeDelta));
+ ASSERT_TRUE(packet4.has_frame_timeline_event());
+
+ const auto& event4 = packet4.frame_timeline_event();
+ ASSERT_TRUE(event4.has_actual_surface_frame_start());
+ const auto& actualSurfaceFrameStart = event4.actual_surface_frame_start();
+ validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart);
+
+ // Packet - 5 : FrameEnd (ActualSurfaceFrame)
+ const auto& packet5 = packets[5];
+ ASSERT_TRUE(packet5.has_timestamp());
+ EXPECT_EQ(packet5.timestamp(), static_cast<uint64_t>(sfStartTime));
+ ASSERT_TRUE(packet5.has_frame_timeline_event());
+
+ const auto& event5 = packet5.frame_timeline_event();
+ ASSERT_TRUE(event5.has_frame_end());
+ const auto& actualSurfaceFrameEnd = event5.frame_end();
+ validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
+}
+
// Tests for Jank classification
TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
// Layer specific increment
@@ -1486,13 +1562,22 @@
/*
* Case 1 - cpu time > vsync period but combined time > deadline > deadline -> cpudeadlinemissed
* Case 2 - cpu time < vsync period but combined time > deadline -> gpudeadlinemissed
+ * Case 3 - previous frame ran longer -> sf_stuffing
+ * Case 4 - Long cpu under SF stuffing -> cpudeadlinemissed
*/
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken3 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+ int64_t sfToken4 = mTokenManager->generateTokenForPredictions({112, 120, 120});
+
// case 1 - cpu time = 33 - 12 = 21, vsync period = 11
mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
@@ -1503,12 +1588,12 @@
// Fences haven't been flushed yet, so it should be 0
EXPECT_EQ(displayFrame0->getActuals().presentTime, 0);
- // case 2 - cpu time = 56 - 52 = 4, vsync period = 11
- mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ // case 2 - cpu time = 56 - 52 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
auto displayFrame1 = getDisplayFrame(1);
- gpuFence2->signalForTest(66);
- presentFence2->signalForTest(71);
+ gpuFence2->signalForTest(76);
+ presentFence2->signalForTest(90);
EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
// Fences have flushed for first displayFrame, so the present timestamps should be updated
@@ -1517,35 +1602,41 @@
EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
- addEmptyDisplayFrame();
+ // case 3 - cpu time = 86 - 82 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfPresent(112, presentFence3, gpuFence3);
+ auto displayFrame2 = getDisplayFrame(2);
+ gpuFence3->signalForTest(116);
+ presentFence3->signalForTest(120);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
// Fences have flushed for second displayFrame, so the present timestamps should be updated
- EXPECT_EQ(displayFrame1->getActuals().presentTime, 71);
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 90);
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
-}
-TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
- auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- mFrameTimeline->setSfWakeUp(sfToken1, 26, Fps::fromPeriodNsecs(11));
- mFrameTimeline->setSfPresent(36, presentFence1);
- auto displayFrame = getDisplayFrame(0);
- presentFence1->signalForTest(52);
+ // case 4 - cpu time = 86 - 82 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken4, 120, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfPresent(140, presentFence4, gpuFence4);
+ auto displayFrame3 = getDisplayFrame(3);
+ gpuFence4->signalForTest(156);
+ presentFence4->signalForTest(180);
- // Fences haven't been flushed yet, so it should be 0
- EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(displayFrame3->getActuals().presentTime, 0);
+ // Fences have flushed for third displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing);
addEmptyDisplayFrame();
- displayFrame = getDisplayFrame(0);
- // Fences have flushed, so the present timestamps should be updated
- EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
- EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::LateStart);
- EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
- EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
- EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+ // Fences have flushed for third displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame3->getActuals().presentTime, 180);
+ EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 6b82170..cbf8cc2 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -37,6 +37,7 @@
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockHWC2.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
@@ -120,13 +121,19 @@
static constexpr hal::HWLayerId kLayerId = static_cast<hal::HWLayerId>(1002);
HWComposerLayerTest(const std::unordered_set<hal::Capability>& capabilities)
- : mCapabilies(capabilities) {}
+ : mCapabilies(capabilities) {
+ EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(kDisplayId));
+ }
- ~HWComposerLayerTest() override { EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId)); }
+ ~HWComposerLayerTest() override {
+ EXPECT_CALL(mDisplay, onLayerDestroyed(kLayerId));
+ EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId));
+ }
std::unique_ptr<Hwc2::mock::Composer> mHal{new StrictMock<Hwc2::mock::Composer>()};
const std::unordered_set<hal::Capability> mCapabilies;
- HWC2::impl::Layer mLayer{*mHal, mCapabilies, kDisplayId, kLayerId};
+ StrictMock<HWC2::mock::Display> mDisplay;
+ HWC2::impl::Layer mLayer{*mHal, mCapabilies, mDisplay, kLayerId};
};
struct HWComposerLayerGenericMetadataTest : public HWComposerLayerTest {
@@ -176,4 +183,4 @@
}
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 325fb8f..d6ce5e2 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -126,7 +126,7 @@
std::deque<FrameTimeData> frameTimes;
constexpr auto kExpectedFps = Fps(50.0f);
constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
- constexpr auto kSmallPeriod = Fps(150.0f).getPeriodNsecs();
+ constexpr auto kSmallPeriod = Fps(250.0f).getPeriodNsecs();
constexpr int kNumIterations = 10;
for (int i = 1; i <= kNumIterations; i++) {
frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 8208b3f..dbd51fe 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -104,8 +104,12 @@
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
}
TEST_F(MessageQueueTest, invalidateTwice) {
@@ -114,11 +118,15 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
}
TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
@@ -127,8 +135,10 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
const auto startTime = 100;
const auto endTime = startTime + mDuration.count();
@@ -141,12 +151,14 @@
EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+ EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
const auto timingAfterCallback =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = presentTime};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
}
@@ -158,7 +170,7 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
}
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index cfbb3f5..6916764 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -19,9 +19,11 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
+#include <utils/Timers.h>
#include "AsyncCallRecorder.h"
#include "Scheduler/OneShotTimer.h"
+#include "fake/FakeClock.h"
using namespace std::chrono_literals;
@@ -33,16 +35,6 @@
OneShotTimerTest() = default;
~OneShotTimerTest() override = default;
- // This timeout should be used when a 3ms callback is expected.
- // While the tests typically request a callback after 3ms, the scheduler
- // does not always cooperate, at it can take significantly longer (observed
- // 30ms).
- static constexpr auto waitTimeForExpected3msCallback = 100ms;
-
- // This timeout should be used when an 3ms callback is not expected.
- // Note that there can be false-negatives if the callback happens later.
- static constexpr auto waitTimeForUnexpected3msCallback = 6ms;
-
AsyncCallRecorder<void (*)()> mResetTimerCallback;
AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
@@ -56,162 +48,179 @@
namespace {
TEST_F(OneShotTimerTest, createAndDestroyTest) {
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
- "TestTimer", 3ms, [] {}, [] {});
+ "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<fake::FakeClock>(clock));
}
TEST_F(OneShotTimerTest, startStopTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 30ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
- auto startTime = std::chrono::steady_clock::now();
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- // The idle timer fires after 30ms, so there should be no callback within
- // 25ms (waiting for a callback for the full 30ms would be problematic).
- bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
- // Under ideal conditions there should be no event. But occasionally
- // it is possible that the wait just prior takes more than 30ms, and
- // a callback is observed. We check the elapsed time since before the OneShotTimer
- // thread was started as a sanity check to not have a flakey test.
- EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
- std::this_thread::sleep_for(std::chrono::milliseconds(25));
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->stop();
}
TEST_F(OneShotTimerTest, resetTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 20ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
+
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- // Observe any event that happens in about 25ms. We don't care if one was
- // observed or not.
- mExpiredTimerCallback.waitForCall(25ms).has_value();
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->reset();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- // There may have been a race with the reset. Clear any callbacks we
- // received right afterwards.
- clearPendingCallbacks();
- // A single callback should be generated after 30ms
- EXPECT_TRUE(
- mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
- // After one event, it should be idle, and not generate another.
- EXPECT_FALSE(
- mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback * 10).has_value());
- mIdleTimer->stop();
- // Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, resetBackToBackTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 20ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
- // A single callback should be generated after 30ms
- EXPECT_TRUE(
- mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
mIdleTimer->stop();
+ clock->advanceTime(2ms);
// Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, startNotCalledTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
// The start hasn't happened, so the callback does not happen.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->stop();
+ clock->advanceTime(2ms);
// Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
- // A callback should be generated after 3ms
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
- // After one event, it should be idle, and not generate another.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- // Once reset, it should generate another
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+
mIdleTimer->reset();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
+ clock->advanceTime(2ms);
// Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
- clearPendingCallbacks();
mIdleTimer->reset();
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->stop();
- clearPendingCallbacks();
mIdleTimer->reset();
+ clock->advanceTime(2ms);
// No more idle events should be observed
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 0b70f27..7ace70a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1450,12 +1450,12 @@
.seamlessness = Seamlessness::OnlySeamless,
.weight = 1.0f,
.focused = true}};
- auto& seamedLayer = layers[0];
ASSERT_EQ(HWC_CONFIG_ID_50,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+ auto& seamedLayer = layers[0];
seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = Fps(30.0f);
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30);
@@ -1464,6 +1464,27 @@
.getModeId());
}
+TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.name = "Min",
+ .vote = LayerVoteType::Min,
+ .weight = 1.f,
+ .focused = true}};
+
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_90Device,
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index c1f0c4e..3e4e130 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -23,10 +23,10 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <TimeStats/TimeStats.h>
-#include <android/util/ProtoOutputStream.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
+#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -156,44 +156,8 @@
}
std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
-
- class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
- public:
- FakeStatsEventDelegate() = default;
- ~FakeStatsEventDelegate() override = default;
-
- struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
- return mEvent;
- }
- void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
- AStatsManager_PullAtomCallback callback,
- void* cookie) override {
- mAtomTags.push_back(atom_tag);
- mCallback = callback;
- mCookie = cookie;
- }
-
- AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atom_tag, void* cookie) {
- return (*mCallback)(atom_tag, nullptr, cookie);
- }
-
- MOCK_METHOD1(clearStatsPullAtomCallback, void(int32_t));
- MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
- MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
- MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
- MOCK_METHOD2(statsEventWriteString8, void(AStatsEvent*, const char*));
- MOCK_METHOD3(statsEventWriteByteArray, void(AStatsEvent*, const uint8_t*, size_t));
- MOCK_METHOD1(statsEventBuild, void(AStatsEvent*));
-
- AStatsEvent* mEvent = AStatsEvent_obtain();
- std::vector<int32_t> mAtomTags;
- AStatsManager_PullAtomCallback mCallback = nullptr;
- void* mCookie = nullptr;
- };
- FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
std::unique_ptr<TimeStats> mTimeStats =
- std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
- std::nullopt, std::nullopt);
+ std::make_unique<impl::TimeStats>(std::nullopt, std::nullopt);
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -278,21 +242,6 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
- mTimeStats->onBootFinished();
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
-}
-
-TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
- EXPECT_CALL(*mDelegate,
- clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
- EXPECT_CALL(*mDelegate,
- clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- mTimeStats.reset();
-}
-
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
@@ -948,24 +897,24 @@
EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
- std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "jankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
+ std::string expectedResult = "totalTimelineFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "jankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfLongCpuJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfLongGpuJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfUnattributedJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "appUnattributedJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfSchedulingJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfPredictionErrorJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "appBufferStuffingJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
}
TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -1012,61 +961,49 @@
}
namespace {
-std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
- const std::vector<int32_t>& frameCounts) {
- util::ProtoOutputStream proto;
+FrameTimingHistogram buildExpectedHistogram(const std::vector<int32_t>& times,
+ const std::vector<int32_t>& frameCounts) {
+ FrameTimingHistogram histogram;
for (int i = 0; i < times.size(); i++) {
ALOGE("Writing time: %d", times[i]);
- proto.write(util::FIELD_TYPE_INT32 | util::FIELD_COUNT_REPEATED | 1 /* field id */,
- (int32_t)times[i]);
+ histogram.add_time_millis_buckets(times[i]);
ALOGE("Writing count: %d", frameCounts[i]);
- proto.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED | 2 /* field id */,
- (int64_t)frameCounts[i]);
+ histogram.add_frame_counts((int64_t)frameCounts[i]);
}
- std::string byteString;
- proto.serializeToString(&byteString);
- return byteString;
+ return histogram;
}
-
-std::string frameRateVoteToProtoByteString(
- float refreshRate,
- TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
- TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
- util::ProtoOutputStream proto;
- proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
- proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
- static_cast<int>(frameRateCompatibility));
- proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
-
- std::string byteString;
- proto.serializeToString(&byteString);
- return byteString;
-}
-
-std::string dumpByteStringHex(const std::string& str) {
- std::stringstream ss;
- ss << std::hex;
- for (const char& c : str) {
- ss << (int)c << " ";
- }
-
- return ss.str();
-}
-
} // namespace
-MATCHER_P2(BytesEq, bytes, size, "") {
- std::string expected;
- expected.append((const char*)bytes, size);
- std::string actual;
- actual.append((const char*)arg, size);
+MATCHER_P(HistogramEq, expected, "") {
+ *result_listener << "Histograms are not equal! \n";
- *result_listener << "Bytes are not equal! \n";
- *result_listener << "size: " << size << "\n";
- *result_listener << "expected: " << dumpByteStringHex(expected).c_str() << "\n";
- *result_listener << "actual: " << dumpByteStringHex(actual).c_str() << "\n";
+ if (arg.time_millis_buckets_size() != expected.time_millis_buckets_size()) {
+ *result_listener << "Time millis bucket are different sizes. Expected: "
+ << expected.time_millis_buckets_size() << ". Actual "
+ << arg.time_millis_buckets_size();
+ return false;
+ }
+ if (arg.frame_counts_size() != expected.frame_counts_size()) {
+ *result_listener << "Frame counts are different sizes. Expected: "
+ << expected.frame_counts_size() << ". Actual " << arg.frame_counts_size();
+ return false;
+ }
- return expected == actual;
+ for (int i = 0; i < expected.time_millis_buckets_size(); i++) {
+ if (arg.time_millis_buckets(i) != expected.time_millis_buckets(i)) {
+ *result_listener << "time_millis_bucket[" << i
+ << "] is different. Expected: " << expected.time_millis_buckets(i)
+ << ". Actual: " << arg.time_millis_buckets(i);
+ return false;
+ }
+ if (arg.frame_counts(i) != expected.frame_counts(i)) {
+ *result_listener << "frame_counts[" << i
+ << "] is different. Expected: " << expected.frame_counts(i)
+ << ". Actual: " << arg.frame_counts(i);
+ return false;
+ }
+ }
+ return true;
}
TEST_F(TimeStatsTest, globalStatsCallback) {
@@ -1074,8 +1011,10 @@
constexpr size_t MISSED_FRAMES = 4;
constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
+ constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+ constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+ constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000;
- mTimeStats->onBootFinished();
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
for (size_t i = 0; i < TOTAL_FRAMES; i++) {
@@ -1100,86 +1039,65 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerCpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerGpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, 1, 2, 3});
+ JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed, 1, 2, 3});
+ JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerScheduling, 1, 2, 3});
+ JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, 1, 2, 3});
+ JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
- 3});
+ JankType::AppDeadlineMissed | JankType::BufferStuffing,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::None, 1, 2, 3});
+ JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::None, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
- std::string expectedFrameDuration = buildExpectedHistogramBytestring({2}, {1});
- std::string expectedRenderEngineTiming = buildExpectedHistogramBytestring({1, 2}, {1, 1});
- std::string expectedEmptyHistogram = buildExpectedHistogramBytestring({}, {});
- std::string expectedSfDeadlineMissed = buildExpectedHistogramBytestring({1}, {7});
- std::string expectedSfPredictionErrors = buildExpectedHistogramBytestring({2}, {7});
+ android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsGlobalInfo& atom = atomList.atom(0);
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, TOTAL_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, MISSED_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, DISPLAY_EVENT_CONNECTIONS));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedFrameDuration.c_str(),
- expectedFrameDuration.size()),
- expectedFrameDuration.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedRenderEngineTiming.c_str(),
- expectedRenderEngineTiming.size()),
- expectedRenderEngineTiming.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 8));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 7));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 2));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, REFRESH_RATE_BUCKET_0));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedSfDeadlineMissed.c_str(),
- expectedSfDeadlineMissed.size()),
- expectedSfDeadlineMissed.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedSfPredictionErrors.c_str(),
- expectedSfPredictionErrors.size()),
- expectedSfPredictionErrors.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, RENDER_RATE_BUCKET_0));
-
- EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- mDelegate->mCookie));
+ EXPECT_EQ(atom.total_frames(), TOTAL_FRAMES);
+ EXPECT_EQ(atom.missed_frames(), MISSED_FRAMES);
+ EXPECT_EQ(atom.client_composition_frames(), CLIENT_COMPOSITION_FRAMES);
+ // Display on millis is not checked.
+ EXPECT_EQ(atom.animation_millis(), 2);
+ EXPECT_EQ(atom.event_connection_count(), DISPLAY_EVENT_CONNECTIONS);
+ EXPECT_THAT(atom.frame_duration(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom.render_engine_timing(), HistogramEq(buildExpectedHistogram({1, 2}, {1, 1})));
+ EXPECT_EQ(atom.total_timeline_frames(), 9);
+ EXPECT_EQ(atom.total_janky_frames(), 7);
+ EXPECT_EQ(atom.total_janky_frames_with_long_cpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_with_long_gpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_sf_unattributed(), 1);
+ EXPECT_EQ(atom.total_janky_frames_app_unattributed(), 2);
+ EXPECT_EQ(atom.total_janky_frames_sf_scheduling(), 1);
+ EXPECT_EQ(atom.total_jank_frames_sf_prediction_error(), 1);
+ EXPECT_EQ(atom.total_jank_frames_app_buffer_stuffing(), 2);
+ EXPECT_EQ(atom.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_THAT(atom.sf_deadline_misses(), HistogramEq(buildExpectedHistogram({1}, {7})));
+ EXPECT_THAT(atom.sf_prediction_errors(), HistogramEq(buildExpectedHistogram({2}, {7})));
+ EXPECT_EQ(atom.render_rate_bucket(), RENDER_RATE_BUCKET_0);
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -1194,7 +1112,7 @@
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "totalTimelineFrames = " + std::to_string(8);
+ expectedResult = "totalTimelineFrames = " + std::to_string(9);
EXPECT_THAT(result, HasSubstr(expectedResult));
expectedResult = "jankyFrames = " + std::to_string(0);
EXPECT_THAT(result, HasSubstr(expectedResult));
@@ -1226,17 +1144,19 @@
EXPECT_THAT(result, HasSubstr(expectedResult));
expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(1);
+ expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(2);
EXPECT_THAT(result, HasSubstr(expectedResult));
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
constexpr size_t LATE_ACQUIRE_FRAMES = 2;
constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
+ constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+ constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+ constexpr nsecs_t APP_DEADLINE_DELTA_2MS = 2'000'000;
+ constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
@@ -1253,116 +1173,69 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerCpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerGpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, 1, 2, 3});
+ JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed, 1, 2, 3});
+ JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerScheduling, 1, 2, 2});
+ JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, 1, 2, 2});
+ JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
- 2});
+ 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),
- JankType::None, 1, 2, 3});
+ JankType::None, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {1});
- std::string expectedPostToPresent = buildExpectedHistogramBytestring({4}, {1});
- std::string expectedAcquireToPresent = buildExpectedHistogramBytestring({3}, {1});
- std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
- std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
- std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
- std::string expectedFrameRateOverride =
- frameRateVoteToProtoByteString(frameRate60.frameRate,
- frameRate60.frameRateCompatibility,
- frameRate60.seamlessness);
- std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3});
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent,
- StrEq(genLayerName(LAYER_ID_0).c_str())));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 0));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedPresentToPresent.c_str(),
- expectedPresentToPresent.size()),
- expectedPresentToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedPostToPresent.c_str(),
- expectedPostToPresent.size()),
- expectedPostToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedAcquireToPresent.c_str(),
- expectedAcquireToPresent.size()),
- expectedAcquireToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedLatchToPresent.c_str(),
- expectedLatchToPresent.size()),
- expectedLatchToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedDesiredToPresent.c_str(),
- expectedDesiredToPresent.size()),
- expectedDesiredToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
- expectedPostToAcquire.size()),
- expectedPostToAcquire.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
- EXPECT_CALL(*mDelegate,
- statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, UID_0));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 8));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 7));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 2));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, REFRESH_RATE_BUCKET_0));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, RENDER_RATE_BUCKET_0));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedFrameRateOverride.c_str(),
- expectedFrameRateOverride.size()),
- expectedFrameRateOverride.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedAppDeadlineMissed.c_str(),
- expectedAppDeadlineMissed.size()),
- expectedAppDeadlineMissed.size()));
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
- EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ EXPECT_EQ(atom.layer_name(), genLayerName(LAYER_ID_0));
+ EXPECT_EQ(atom.total_frames(), 1);
+ EXPECT_EQ(atom.dropped_frames(), 0);
+ EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+ EXPECT_THAT(atom.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+ EXPECT_THAT(atom.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_EQ(atom.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
+ EXPECT_EQ(atom.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_EQ(atom.uid(), UID_0);
+ EXPECT_EQ(atom.total_timeline_frames(), 8);
+ EXPECT_EQ(atom.total_janky_frames(), 7);
+ EXPECT_EQ(atom.total_janky_frames_with_long_cpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_with_long_gpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_sf_unattributed(), 1);
+ EXPECT_EQ(atom.total_janky_frames_app_unattributed(), 2);
+ EXPECT_EQ(atom.total_janky_frames_sf_scheduling(), 1);
+ EXPECT_EQ(atom.total_jank_frames_sf_prediction_error(), 1);
+ EXPECT_EQ(atom.total_jank_frames_app_buffer_stuffing(), 1);
+ EXPECT_EQ(atom.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_EQ(atom.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+ EXPECT_THAT(atom.set_frame_rate_vote().frame_rate(), testing::FloatEq(frameRate60.frameRate));
+ EXPECT_EQ((int)atom.set_frame_rate_vote().frame_rate_compatibility(),
+ (int)frameRate60.frameRateCompatibility);
+ EXPECT_EQ((int)atom.set_frame_rate_vote().seamlessness(), (int)frameRate60.seamlessness);
+ EXPECT_THAT(atom.app_deadline_misses(), HistogramEq(buildExpectedHistogram({3, 2}, {4, 3})));
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -1398,37 +1271,26 @@
TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO))
- .Times(2);
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_0).c_str())));
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 2);
+ std::vector<std::string> actualLayerNames = {atomList.atom(0).layer_name(),
+ atomList.atom(1).layer_name()};
+ EXPECT_THAT(actualLayerNames,
+ UnorderedElementsAre(genLayerName(LAYER_ID_0), genLayerName(LAYER_ID_1)));
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
@@ -1438,102 +1300,53 @@
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1, 2}, {2, 1});
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedPresentToPresent.c_str(),
- expectedPresentToPresent.size()),
- expectedPresentToPresent.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
- .Times(AnyNumber());
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+ EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1, 2}, {2, 1})));
}
TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) {
- mDelegate = new FakeStatsEventDelegate;
- mTimeStats =
- std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
- std::nullopt, 1);
+ mTimeStats = std::make_unique<impl::TimeStats>(std::nullopt, 1);
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {2});
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedPresentToPresent.c_str(),
- expectedPresentToPresent.size()),
- expectedPresentToPresent.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
- .Times(AnyNumber());
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+ EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {2})));
}
TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) {
- mDelegate = new FakeStatsEventDelegate;
- mTimeStats =
- std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate), 1,
- std::nullopt);
+ mTimeStats = std::make_unique<impl::TimeStats>(1, std::nullopt);
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO))
- .Times(1);
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ EXPECT_EQ(atomList.atom(0).layer_name(), genLayerName(LAYER_ID_1));
}
TEST_F(TimeStatsTest, canSurviveMonkey) {
@@ -1563,6 +1376,30 @@
}
}
+TEST_F(TimeStatsTest, refreshRateIsClampedToNearestBucket) {
+ // this stat is not in the proto so verify by checking the string dump
+ const auto verifyRefreshRateBucket = [&](Fps fps, int32_t bucket) {
+ EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ mTimeStats->incrementJankyFrames(
+ {fps, std::nullopt, UID_0, genLayerName(LAYER_ID_0), JankType::None, 0, 0, 0});
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "displayRefreshRate = " + std::to_string(bucket) + " fps";
+ EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps;
+ };
+
+ verifyRefreshRateBucket(Fps(91.f), 90);
+ verifyRefreshRateBucket(Fps(89.f), 90);
+
+ verifyRefreshRateBucket(Fps(61.f), 60);
+ verifyRefreshRateBucket(Fps(59.f), 60);
+
+ verifyRefreshRateBucket(Fps(31.f), 30);
+ verifyRefreshRateBucket(Fps(29.f), 30);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index b64cce9..d59d64b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -232,11 +232,12 @@
VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
mVsyncMoveThreshold};
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
}
}
@@ -245,11 +246,13 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = intended});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
+
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -277,11 +280,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = workDuration,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = workDuration,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -289,11 +293,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
}
@@ -302,11 +306,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
mMockClock.advanceBy(950);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
}
@@ -316,11 +320,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -337,11 +341,11 @@
PausingCallback cb(mDispatch, 50ms);
cb.stashResource(resource);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -535,21 +539,25 @@
std::optional<nsecs_t> lastTarget;
tmp = mDispatch.registerCallback(
[&](auto timestamp, auto, auto) {
- EXPECT_EQ(mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp - mVsyncMoveThreshold}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp + mVsyncMoveThreshold}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp - mVsyncMoveThreshold});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ result = mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ result = mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp + mVsyncMoveThreshold});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod + timestamp - 400, *result);
lastTarget = timestamp;
},
"oo");
@@ -627,36 +635,41 @@
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
VSyncDispatch::CallbackToken token(100);
- EXPECT_THAT(mDispatch.schedule(token,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 1000}),
- Eq(ScheduleResult::Error));
+ EXPECT_FALSE(mDispatch
+ .schedule(token,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+ .has_value());
EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
}
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
+ result = mDispatch.schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
}
// b/1450138150
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1200, *result);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
}
@@ -667,24 +680,30 @@
.WillOnce(Return(1000))
.WillOnce(Return(1002));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(602, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb0,
+ {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -692,26 +711,32 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb0,
+ {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1100, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
EXPECT_CALL(mMockClock, alarmAt(_, 600));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
advanceToNextCallback();
}
@@ -754,16 +779,19 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1900, *result);
mMockClock.advanceBy(80);
EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -779,16 +807,19 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1630, *result);
mMockClock.advanceBy(80);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -802,12 +833,15 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1900, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -828,12 +862,15 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1900, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -861,12 +898,15 @@
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(610, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(700);
@@ -886,11 +926,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 70,
- .readyDuration = 30,
- .earliestVsync = intended}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = 70,
+ .readyDuration = 30,
+ .earliestVsync = intended});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -922,9 +963,9 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -944,9 +985,9 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
- mStubTracker, now),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+ mStubTracker, now)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(9500));
@@ -967,9 +1008,9 @@
},
mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -1002,9 +1043,9 @@
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
@@ -1018,9 +1059,9 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
entry.update(mStubTracker, 0);
auto const wakeup = entry.wakeupTime();
@@ -1031,26 +1072,26 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+ mStubTracker, 0)
+ .has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
}
@@ -1071,32 +1112,32 @@
.InSequence(seq)
.WillOnce(Return(2000));
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
entry.executing(); // 1000 is executing
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
}
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
+ EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
}
TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
@@ -1128,9 +1169,9 @@
},
mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
diff --git a/services/surfaceflinger/tests/unittests/fake/FakeClock.h b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
new file mode 100644
index 0000000..6d9c764
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/fake/FakeClock.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 "../../Clock.h"
+
+namespace android::fake {
+
+class FakeClock : public Clock {
+public:
+ virtual ~FakeClock() = default;
+ std::chrono::steady_clock::time_point now() const override { return mNow; }
+
+ void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; }
+
+private:
+ std::chrono::steady_clock::time_point mNow;
+};
+
+} // namespace android::fake
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp
new file mode 100644
index 0000000..2647bf4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "MockHWC2"
+
+#include "mock/DisplayHardware/MockHWC2.h"
+
+namespace android::HWC2::mock {
+
+// Explicit default instantiation is recommended.
+Display::Display() = default;
+Display::~Display() = default;
+
+Layer::Layer() = default;
+Layer::~Layer() = default;
+
+} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
new file mode 100644
index 0000000..c3919d9
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -0,0 +1,122 @@
+/*
+ * 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 <gmock/gmock.h>
+
+#include "DisplayHardware/HWC2.h"
+
+namespace android::HWC2::mock {
+
+class Display : public HWC2::Display {
+public:
+ Display();
+ ~Display() override;
+
+ MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override));
+ MOCK_METHOD(bool, isConnected, (), (const, override));
+ MOCK_METHOD(void, setConnected, (bool), (override));
+ MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (),
+ (const, override));
+ MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
+ MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));
+
+ MOCK_METHOD(hal::Error, acceptChanges, (), (override));
+ 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));
+ 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> *),
+ (const, override));
+ MOCK_METHOD(hal::Error, getDataspaceSaturationMatrix, (hal::Dataspace, android::mat4 *),
+ (override));
+ MOCK_METHOD(hal::Error, getName, (std::string *), (const, override));
+ MOCK_METHOD(hal::Error, getRequests,
+ (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)),
+ (override));
+ MOCK_METHOD(hal::Error, getConnectionType, (ui::DisplayConnectionType *), (const, override));
+ MOCK_METHOD(hal::Error, supportsDoze, (bool *), (const, override));
+ MOCK_METHOD(hal::Error, getHdrCapabilities, (android::HdrCapabilities *), (const, override));
+ MOCK_METHOD(hal::Error, getDisplayedContentSamplingAttributes,
+ (hal::PixelFormat *, hal::Dataspace *, uint8_t *), (const, override));
+ MOCK_METHOD(hal::Error, setDisplayContentSamplingEnabled, (bool, uint8_t, uint64_t),
+ (const, override));
+ MOCK_METHOD(hal::Error, getDisplayedContentSample,
+ (uint64_t, uint64_t, android::DisplayedFrameStats *), (const, override));
+ MOCK_METHOD(hal::Error, getReleaseFences,
+ ((std::unordered_map<Layer *, android::sp<android::Fence>> *)), (const, override));
+ MOCK_METHOD(hal::Error, present, (android::sp<android::Fence> *), (override));
+ MOCK_METHOD(hal::Error, setClientTarget,
+ (uint32_t, const android::sp<android::GraphicBuffer> &,
+ 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, 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, presentOrValidate,
+ (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 &,
+ hal::VsyncPeriodChangeTimeline *),
+ (override));
+ MOCK_METHOD(hal::Error, setAutoLowLatencyMode, (bool), (override));
+ 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));
+};
+
+class Layer : public HWC2::Layer {
+public:
+ Layer();
+ ~Layer() override;
+
+ MOCK_METHOD(hal::HWLayerId, getId, (), (const, override));
+ MOCK_METHOD(hal::Error, setCursorPosition, (int32_t, int32_t), (override));
+ MOCK_METHOD(hal::Error, setBuffer,
+ (uint32_t, const android::sp<android::GraphicBuffer> &,
+ const android::sp<android::Fence> &),
+ (override));
+ 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, setDataspace, (android::ui::Dataspace), (override));
+ MOCK_METHOD(hal::Error, setPerFrameMetadata, (const int32_t, const android::HdrMetadata &),
+ (override));
+ MOCK_METHOD(hal::Error, setDisplayFrame, (const android::Rect &), (override));
+ MOCK_METHOD(hal::Error, setPlaneAlpha, (float), (override));
+ MOCK_METHOD(hal::Error, setSidebandStream, (const native_handle_t *), (override));
+ MOCK_METHOD(hal::Error, setSourceCrop, (const android::FloatRect &), (override));
+ MOCK_METHOD(hal::Error, setTransform, (hal::Transform), (override));
+ MOCK_METHOD(hal::Error, setVisibleRegion, (const android::Region &), (override));
+ MOCK_METHOD(hal::Error, setZOrder, (uint32_t), (override));
+ MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
+ MOCK_METHOD(hal::Error, setLayerGenericMetadata,
+ (const std::string &, bool, const std::vector<uint8_t> &), (override));
+};
+
+} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 453c93a..0e7b320 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -39,6 +39,7 @@
void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds));
MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
+ MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>());
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 37b74ed..526a847 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
TimeStats();
~TimeStats() override;
- MOCK_METHOD0(onBootFinished, void());
+ MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index eb24a22..80e9a3c 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -106,42 +106,6 @@
],
}
-cc_binary {
- name: "vr_hwc",
- enabled: false,
- system_ext_specific: true,
- vintf_fragments: ["manifest_vr_hwc.xml"],
- srcs: [
- "vr_hardware_composer_service.cpp",
- ],
- static_libs: [
- "libvr_hwc-impl",
- // NOTE: This needs to be included after the *-impl lib otherwise the
- // symbols in the *-binder library get optimized out.
- "libvr_hwc-binder",
- ],
- shared_libs: [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.composer@2.3",
- "libbase",
- "libbinder",
- "liblog",
- "libhardware",
- "libhidlbase",
- "libui",
- "libutils",
- "libvr_hwc-hal",
- ],
- cflags: [
- "-DLOG_TAG=\"vr_hwc\"",
- "-Wall",
- "-Werror",
- ],
- init_rc: [
- "vr_hwc.rc",
- ],
-}
-
cc_test {
name: "vr_hwc_test",
gtest: true,
diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml
deleted file mode 100644
index 1068cac..0000000
--- a/services/vr/hardware_composer/manifest_vr_hwc.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
- <hal>
- <name>android.hardware.graphics.composer</name>
- <transport>hwbinder</transport>
- <version>2.1</version>
- <interface>
- <name>IComposer</name>
- <instance>vr</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/services/vr/hardware_composer/vr_hardware_composer_service.cpp b/services/vr/hardware_composer/vr_hardware_composer_service.cpp
deleted file mode 100644
index 7701847..0000000
--- a/services/vr/hardware_composer/vr_hardware_composer_service.cpp
+++ /dev/null
@@ -1,55 +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.
- */
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <hwbinder/IPCThreadState.h>
-#include <impl/vr_hwc.h>
-#include <inttypes.h>
-
-#include "vr_composer.h"
-
-int main() {
- android::ProcessState::self()->startThreadPool();
-
- // Register the hwbinder HWC HAL service used by SurfaceFlinger while in VR
- // mode.
- android::sp<android::dvr::VrHwc> service = new android::dvr::VrHwc();
-
- LOG_ALWAYS_FATAL_IF(!service.get(), "Failed to get service");
- LOG_ALWAYS_FATAL_IF(service->isRemote(), "Service is remote");
-
- const char instance[] = "vr";
- LOG_ALWAYS_FATAL_IF(service->registerAsService(instance) != android::OK,
- "Failed to register service");
-
- android::sp<android::dvr::VrComposer> composer =
- new android::dvr::VrComposer(service.get());
-
- android::sp<android::IServiceManager> sm(android::defaultServiceManager());
-
- // Register the binder service used by VR Window Manager service to receive
- // frame information from VR HWC HAL.
- android::status_t status = sm->addService(
- android::dvr::VrComposer::SERVICE_NAME(), composer.get(),
- false /* allowIsolated */);
- LOG_ALWAYS_FATAL_IF(status != android::OK,
- "VrDisplay service failed to start: %" PRId32, status);
-
- android::hardware::ProcessState::self()->startThreadPool();
- android::hardware::IPCThreadState::self()->joinThreadPool();
-
- return 0;
-}
diff --git a/services/vr/hardware_composer/vr_hwc.rc b/services/vr/hardware_composer/vr_hwc.rc
deleted file mode 100644
index 645ab80..0000000
--- a/services/vr/hardware_composer/vr_hwc.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service vr_hwc /system/bin/vr_hwc
- class hal animation
- user system
- group system graphics
- onrestart restart surfaceflinger
- writepid /dev/cpuset/system-background/tasks
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 67cd875..440c5b1 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -29,17 +29,14 @@
unversioned_until: "current",
}
-llndk_library {
- name: "libvulkan.llndk",
- symbol_file: "libvulkan.map.txt",
- export_llndk_headers: [
- "vulkan_headers_llndk",
- ],
-}
-
cc_library_shared {
name: "libvulkan",
- llndk_stubs: "libvulkan.llndk",
+ llndk: {
+ symbol_file: "libvulkan.map.txt",
+ export_llndk_headers: [
+ "vulkan_headers",
+ ],
+ },
clang: true,
sanitize: {
misc_undefined: ["integer"],