Merge changes from topics "presubmit-am-2cdd826ba79049868e8caf3315eb6c80", "presubmit-am-417dfb9fcae14774a7ec06fd778d56c5" into tm-dev
* changes:
swapchain: Implement VK_KHR_surface_protected_capabilities
swapchain: Implement VK_GOOGLE_surfaceless_query
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7bde21..3d4ba6f 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -723,10 +723,10 @@
}
// TODO(b/220095381): Due to boot time regression, we have omitted call to
- // createAppDirectoryForSupplementalData from here temporarily (unless it's for testing)
+ // createSdkSandboxDataDirectory from here temporarily (unless it's for testing)
if (uuid_ != nullptr && strcmp(uuid_, "TEST") == 0) {
- auto status = createAppDirectoryForSupplementalData(uuid, packageName, userId, appId,
- previousAppId, seInfo, flags);
+ auto status = createSdkSandboxDataDirectory(uuid, packageName, userId, appId, previousAppId,
+ seInfo, flags);
if (!status.isOk()) {
return status;
}
@@ -736,15 +736,15 @@
}
/**
- * Responsible for creating /data/user/0/supplemental/<app-name> directory and other
+ * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<app-name> directory and other
* app level sub directories, such as ./shared
*/
-binder::Status InstalldNativeService::createAppDirectoryForSupplementalData(
+binder::Status InstalldNativeService::createSdkSandboxDataDirectory(
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t flags) {
- int32_t supplementalUid = multiuser_get_supplemental_uid(userId, appId);
- if (supplementalUid == -1) {
- // There no valid supplemental process for this app. Skip creation of data directory
+ int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
+ if (sdkSandboxUid == -1) {
+ // There no valid sdk sandbox process for this app. Skip creation of data directory
return ok();
}
@@ -759,35 +759,35 @@
}
bool isCeData = (currentFlag == FLAG_STORAGE_CE);
- // /data/misc_{ce,de}/<user-id>/supplemental directory gets created by vold
+ // /data/misc_{ce,de}/<user-id>/sdksandbox directory gets created by vold
// during user creation
// Prepare the app directory
- auto appPath = create_data_misc_supplemental_package_path(uuid_, isCeData, userId,
- packageName.c_str());
+ auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+ packageName.c_str());
if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) {
return error("Failed to prepare " + appPath);
}
// Now prepare the shared directory which will be accessible by all codes
- auto sharedPath = create_data_misc_supplemental_shared_path(uuid_, isCeData, userId,
- packageName.c_str());
+ auto sharedPath = create_data_misc_sdk_sandbox_shared_path(uuid_, isCeData, userId,
+ packageName.c_str());
- int32_t previousSupplementalUid = multiuser_get_supplemental_uid(userId, previousAppId);
+ int32_t previousSdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId);
int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
if (cacheGid == -1) {
return exception(binder::Status::EX_ILLEGAL_STATE,
- StringPrintf("cacheGid cannot be -1 for supplemental data"));
+ StringPrintf("cacheGid cannot be -1 for sdksandbox data"));
}
- auto status = createAppDataDirs(sharedPath, supplementalUid, &previousSupplementalUid,
- cacheGid, seInfo, 0700);
+ auto status = createAppDataDirs(sharedPath, sdkSandboxUid, &previousSdkSandboxUid, cacheGid,
+ seInfo, 0700);
if (!status.isOk()) {
return status;
}
// TODO(b/211763739): We also need to handle art profile creations
- // TODO(b/211763739): And return the CE inode of the supplemental root directory and
+ // TODO(b/211763739): And return the CE inode of the sdksandbox root directory and
// app directory under it so we can clear contents while CE storage is locked
}
@@ -2084,6 +2084,10 @@
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
+ if (packageNames.size() != ceDataInodes.size()) {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ "packageNames/ceDataInodes size mismatch.");
+ }
for (const auto& packageName : packageNames) {
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
}
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 6b4ba1f..c9a4d50 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -205,11 +205,10 @@
const std::string& seInfo, int32_t targetSdkVersion,
int64_t* _aidl_return);
- binder::Status createAppDirectoryForSupplementalData(const std::optional<std::string>& uuid,
- const std::string& packageName,
- int32_t userId, int32_t appId,
- int32_t previousAppId,
- const std::string& seInfo, int32_t flags);
+ binder::Status createSdkSandboxDataDirectory(const std::optional<std::string>& uuid,
+ const std::string& packageName, int32_t userId,
+ int32_t appId, int32_t previousAppId,
+ const std::string& seInfo, int32_t flags);
};
} // namespace installd
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index c796da6..9647865 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -74,10 +74,19 @@
using android::base::ReadFully;
using android::base::StringPrintf;
using android::base::WriteFully;
+using android::base::borrowed_fd;
using android::base::unique_fd;
namespace {
+// Timeout for short operations, such as merging profiles.
+constexpr int kShortTimeoutMs = 60000; // 1 minute.
+
+// Timeout for long operations, such as compilation. This should be smaller than the Package Manager
+// watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that the operation will be
+// aborted before that watchdog would take down the system server.
+constexpr int kLongTimeoutMs = 570000; // 9.5 minutes.
+
class DexOptStatus {
public:
// Check if dexopt is cancelled and fork if it is not cancelled.
@@ -486,6 +495,25 @@
}
}
+// Cleans up an output file specified by a file descriptor. This function should be called whenever
+// a subprocess that modifies a system-managed file crashes.
+// If the subprocess crashes while it's writing to the file, the file is likely corrupted, so we
+// should remove it.
+// If the subprocess times out and is killed while it's acquiring a flock on the file, there is
+// probably a deadlock, so it's also good to remove the file so that later operations won't
+// encounter the same problem. It's safe to do so because the process that is holding the flock will
+// still have access to the file until the file descriptor is closed.
+// Note that we can't do `clear_reference_profile` here even if the fd points to a reference profile
+// because that also requires a flock and is therefore likely to be stuck in the second case.
+static bool cleanup_output_fd(int fd) {
+ std::string path;
+ bool ret = remove_file_at_fd(fd, &path);
+ if (ret) {
+ LOG(INFO) << "Removed file at path " << path;
+ }
+ return ret;
+}
+
static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 0;
static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 1;
static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_NOT_ENOUGH_DELTA = 2;
@@ -497,13 +525,14 @@
class RunProfman : public ExecVHelper {
public:
- void SetupArgs(const std::vector<unique_fd>& profile_fds,
- const unique_fd& reference_profile_fd,
- const std::vector<unique_fd>& apk_fds,
- const std::vector<std::string>& dex_locations,
- bool copy_and_update,
- bool for_snapshot,
- bool for_boot_image) {
+ template <typename T, typename U>
+ void SetupArgs(const std::vector<T>& profile_fds,
+ const unique_fd& reference_profile_fd,
+ const std::vector<U>& apk_fds,
+ const std::vector<std::string>& dex_locations,
+ bool copy_and_update,
+ bool for_snapshot,
+ bool for_boot_image) {
// TODO(calin): Assume for now we run in the bg compile job (which is in
// most of the invocation). With the current data flow, is not very easy or
@@ -519,11 +548,11 @@
AddArg("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get()));
}
- for (const unique_fd& fd : profile_fds) {
+ for (const T& fd : profile_fds) {
AddArg("--profile-file-fd=" + std::to_string(fd.get()));
}
- for (const unique_fd& fd : apk_fds) {
+ for (const U& fd : apk_fds) {
AddArg("--apk-fd=" + std::to_string(fd.get()));
}
@@ -582,20 +611,14 @@
for_boot_image);
}
- void SetupCopyAndUpdate(unique_fd&& profile_fd,
- unique_fd&& reference_profile_fd,
- unique_fd&& apk_fd,
+ void SetupCopyAndUpdate(const unique_fd& profile_fd,
+ const unique_fd& reference_profile_fd,
+ const unique_fd& apk_fd,
const std::string& dex_location) {
- // The fds need to stay open longer than the scope of the function, so put them into a local
- // variable vector.
- profiles_fd_.push_back(std::move(profile_fd));
- apk_fds_.push_back(std::move(apk_fd));
- reference_profile_fd_ = std::move(reference_profile_fd);
- std::vector<std::string> dex_locations = {dex_location};
- SetupArgs(profiles_fd_,
- reference_profile_fd_,
- apk_fds_,
- dex_locations,
+ SetupArgs(std::vector<borrowed_fd>{profile_fd},
+ reference_profile_fd,
+ std::vector<borrowed_fd>{apk_fd},
+ {dex_location},
/*copy_and_update=*/true,
/*for_snapshot*/false,
/*for_boot_image*/false);
@@ -621,11 +644,6 @@
void Exec() {
ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
}
-
- private:
- unique_fd reference_profile_fd_;
- std::vector<unique_fd> profiles_fd_;
- std::vector<unique_fd> apk_fds_;
};
static int analyze_profiles(uid_t uid, const std::string& package_name,
@@ -657,13 +675,14 @@
profman_merge.Exec();
}
/* parent */
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
bool need_to_compile = false;
bool empty_profiles = false;
bool should_clear_current_profiles = false;
bool should_clear_reference_profile = false;
if (!WIFEXITED(return_code)) {
LOG(WARNING) << "profman failed for location " << location << ": " << return_code;
+ cleanup_output_fd(reference_profile_fd.get());
} else {
return_code = WEXITSTATUS(return_code);
switch (return_code) {
@@ -797,10 +816,10 @@
profman_dump.Exec();
}
/* parent */
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
if (!WIFEXITED(return_code)) {
- LOG(WARNING) << "profman failed for package " << pkgname << ": "
- << return_code;
+ LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
+ cleanup_output_fd(output_fd.get());
return false;
}
return true;
@@ -871,7 +890,11 @@
_exit(0);
}
/* parent */
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
+ if (!WIFEXITED(return_code)) {
+ cleanup_output_fd(out_fd.get());
+ return false;
+ }
return return_code == 0;
}
@@ -1521,7 +1544,7 @@
}
pipe_read.reset();
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
if (!WIFEXITED(return_code)) {
PLOG(ERROR) << "Error waiting for child dexoptanalyzer process";
return false;
@@ -1695,7 +1718,7 @@
}
/* parent */
- int result = wait_child(pid);
+ int result = wait_child_with_timeout(pid, kShortTimeoutMs);
cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
if (!WIFEXITED(result)) {
if ((WTERMSIG(result) == SIGKILL) && cancelled) {
@@ -1954,7 +1977,7 @@
runner.Exec(DexoptReturnCodes::kDex2oatExec);
} else {
- int res = wait_child(pid);
+ int res = wait_child_with_timeout(pid, kLongTimeoutMs);
bool cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
if (res == 0) {
LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---";
@@ -2143,7 +2166,7 @@
_exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
}
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
if (!WIFEXITED(return_code)) {
LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code;
} else {
@@ -2261,7 +2284,7 @@
if (!ReadFully(pipe_read, out_secondary_dex_hash->data(), out_secondary_dex_hash->size())) {
out_secondary_dex_hash->clear();
}
- return wait_child(pid) == 0;
+ return wait_child_with_timeout(pid, kShortTimeoutMs) == 0;
}
// Helper for move_ab, so that we can have common failure-case cleanup.
@@ -2591,9 +2614,10 @@
}
/* parent */
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
if (!WIFEXITED(return_code)) {
LOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+ cleanup_output_fd(snapshot_fd.get());
return false;
}
@@ -2700,10 +2724,11 @@
}
/* parent */
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
if (!WIFEXITED(return_code)) {
PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+ cleanup_output_fd(snapshot_fd.get());
return false;
}
@@ -2774,9 +2799,9 @@
}
RunProfman args;
- args.SetupCopyAndUpdate(std::move(dex_metadata_fd),
- std::move(ref_profile_fd),
- std::move(apk_fd),
+ args.SetupCopyAndUpdate(dex_metadata_fd,
+ ref_profile_fd,
+ apk_fd,
code_path);
pid_t pid = fork();
if (pid == 0) {
@@ -2789,9 +2814,10 @@
}
/* parent */
- int return_code = wait_child(pid);
+ int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
if (!WIFEXITED(return_code)) {
PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+ cleanup_output_fd(ref_profile_fd.get());
return false;
}
return true;
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index bb36c39..f21a304 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -50,6 +50,8 @@
namespace android {
namespace installd {
+constexpr int kTimeoutMs = 60000;
+
// TODO(calin): try to dedup this code.
#if defined(__arm__)
static const std::string kRuntimeIsa = "arm";
@@ -1073,7 +1075,7 @@
_exit(0);
}
/* parent */
- ASSERT_TRUE(WIFEXITED(wait_child(pid)));
+ ASSERT_TRUE(WIFEXITED(wait_child_with_timeout(pid, kTimeoutMs)));
}
void mergePackageProfiles(const std::string& package_name,
@@ -1377,7 +1379,7 @@
_exit(0);
}
/* parent */
- ASSERT_TRUE(WIFEXITED(wait_child(pid)));
+ ASSERT_TRUE(WIFEXITED(wait_child_with_timeout(pid, kTimeoutMs)));
}
protected:
std::string intial_android_profiles_dir;
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 703a096..21ab5b8 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -81,7 +81,7 @@
static constexpr const uid_t kTestAppId = 19999;
const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
-const uid_t kTestAppSupplementalUid = multiuser_get_supplemental_uid(kTestUserId, kTestAppId);
+const uid_t kTestSdkSandboxUid = multiuser_get_sdk_sandbox_uid(kTestUserId, kTestAppId);
#define FLAG_FORCE InstalldNativeService::FLAG_FORCE
@@ -491,6 +491,19 @@
system(removeCommand.c_str());
}
}
+TEST_F(ServiceTest, GetAppSizeWrongSizes) {
+ int32_t externalStorageAppId = -1;
+ std::vector<int64_t> externalStorageSize;
+
+ std::vector<std::string> codePaths;
+ std::vector<std::string> packageNames = {"package1", "package2"};
+ std::vector<int64_t> ceDataInodes = {0};
+
+ EXPECT_BINDER_FAIL(service->getAppSize(std::nullopt, packageNames, 0,
+ InstalldNativeService::FLAG_USE_QUOTA,
+ externalStorageAppId, ceDataInodes, codePaths,
+ &externalStorageSize));
+}
static bool mkdirs(const std::string& path, mode_t mode) {
struct stat sb;
if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
@@ -936,7 +949,7 @@
"com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
}
-class AppSupplementalDataTest : public testing::Test {
+class SdkSandboxDataTest : public testing::Test {
public:
void CheckFileAccess(const std::string& path, uid_t uid, mode_t mode) {
const auto fullPath = "/data/local/tmp/" + path;
@@ -973,8 +986,8 @@
clearAppData();
ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700));
ASSERT_TRUE(mkdirs("/data/local/tmp/user_de/0", 0700));
- ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/supplemental", 0700));
- ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/supplemental", 0700));
+ ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/sdksandbox", 0700));
+ ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/sdksandbox", 0700));
init_globals_from_data_and_root();
}
@@ -993,7 +1006,7 @@
}
};
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData) {
android::os::CreateAppDataResult result;
android::os::CreateAppDataArgs args = createAppDataArgs();
args.packageName = "com.foo";
@@ -1002,24 +1015,22 @@
// Create the app user data.
ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
- CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
- CheckFileAccess("misc_ce/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
- S_IFDIR | 0700);
- CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700);
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid,
S_IFDIR | S_ISGID | 0771);
- CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid,
S_IFDIR | S_ISGID | 0771);
- CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
- CheckFileAccess("misc_de/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
- S_IFDIR | 0700);
- CheckFileAccess("misc_de/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
+ CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
+ CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700);
+ CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid,
S_IFDIR | S_ISGID | 0771);
- CheckFileAccess("misc_de/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
+ CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid,
S_IFDIR | S_ISGID | 0771);
}
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) {
android::os::CreateAppDataResult result;
android::os::CreateAppDataArgs args = createAppDataArgs();
args.packageName = "com.foo";
@@ -1029,13 +1040,13 @@
ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
// Only CE paths should exist
- CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
// DE paths should not exist
- ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/supplemental/com.foo"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
}
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFlag) {
android::os::CreateAppDataResult result;
android::os::CreateAppDataArgs args = createAppDataArgs();
args.packageName = "com.foo";
@@ -1045,10 +1056,10 @@
ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
// CE paths should not exist
- ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/supplemental/com.foo"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
// Only DE paths should exist
- CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+ CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
}
} // namespace installd
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index a7a8624..17802a3 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <android-base/logging.h>
#include <android-base/scopeguard.h>
@@ -656,38 +658,76 @@
ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700));
}
-TEST_F(UtilsTest, TestSupplementalDataPaths) {
+TEST_F(UtilsTest, TestSdkSandboxDataPaths) {
// Ce data paths
- EXPECT_EQ("/data/misc_ce/0/supplemental",
- create_data_misc_supplemental_path(nullptr, /*isCeData=*/true, 0));
- EXPECT_EQ("/data/misc_ce/10/supplemental",
- create_data_misc_supplemental_path(nullptr, true, 10));
+ EXPECT_EQ("/data/misc_ce/0/sdksandbox",
+ create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/true, 0));
+ EXPECT_EQ("/data/misc_ce/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, true, 10));
- EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo",
- create_data_misc_supplemental_package_path(nullptr, true, 0, "com.foo"));
- EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo",
- create_data_misc_supplemental_package_path(nullptr, true, 10, "com.foo"));
+ EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo",
+ create_data_misc_sdk_sandbox_package_path(nullptr, true, 0, "com.foo"));
+ EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo",
+ create_data_misc_sdk_sandbox_package_path(nullptr, true, 10, "com.foo"));
- EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo/shared",
- create_data_misc_supplemental_shared_path(nullptr, true, 0, "com.foo"));
- EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo/shared",
- create_data_misc_supplemental_shared_path(nullptr, true, 10, "com.foo"));
+ EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo/shared",
+ create_data_misc_sdk_sandbox_shared_path(nullptr, true, 0, "com.foo"));
+ EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared",
+ create_data_misc_sdk_sandbox_shared_path(nullptr, true, 10, "com.foo"));
// De data paths
- EXPECT_EQ("/data/misc_de/0/supplemental",
- create_data_misc_supplemental_path(nullptr, /*isCeData=*/false, 0));
- EXPECT_EQ("/data/misc_de/10/supplemental",
- create_data_misc_supplemental_path(nullptr, false, 10));
+ EXPECT_EQ("/data/misc_de/0/sdksandbox",
+ create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/false, 0));
+ EXPECT_EQ("/data/misc_de/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, false, 10));
- EXPECT_EQ("/data/misc_de/0/supplemental/com.foo",
- create_data_misc_supplemental_package_path(nullptr, false, 0, "com.foo"));
- EXPECT_EQ("/data/misc_de/10/supplemental/com.foo",
- create_data_misc_supplemental_package_path(nullptr, false, 10, "com.foo"));
+ EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo",
+ create_data_misc_sdk_sandbox_package_path(nullptr, false, 0, "com.foo"));
+ EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo",
+ create_data_misc_sdk_sandbox_package_path(nullptr, false, 10, "com.foo"));
- EXPECT_EQ("/data/misc_de/0/supplemental/com.foo/shared",
- create_data_misc_supplemental_shared_path(nullptr, false, 0, "com.foo"));
- EXPECT_EQ("/data/misc_de/10/supplemental/com.foo/shared",
- create_data_misc_supplemental_shared_path(nullptr, false, 10, "com.foo"));
+ EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo/shared",
+ create_data_misc_sdk_sandbox_shared_path(nullptr, false, 0, "com.foo"));
+ EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared",
+ create_data_misc_sdk_sandbox_shared_path(nullptr, false, 10, "com.foo"));
+}
+
+TEST_F(UtilsTest, WaitChild) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child */
+ // Do nothing.
+ _exit(0);
+ }
+ /* parent */
+ int return_code = wait_child_with_timeout(pid, /*timeout_ms=*/100);
+ EXPECT_TRUE(WIFEXITED(return_code));
+ EXPECT_EQ(WEXITSTATUS(return_code), 0);
+}
+
+TEST_F(UtilsTest, WaitChildTimeout) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child */
+ sleep(1);
+ _exit(0);
+ }
+ /* parent */
+ int return_code = wait_child_with_timeout(pid, /*timeout_ms=*/1);
+ EXPECT_FALSE(WIFEXITED(return_code));
+ EXPECT_EQ(WTERMSIG(return_code), SIGKILL);
+}
+
+TEST_F(UtilsTest, RemoveFileAtFd) {
+ std::string filename = "/data/local/tmp/tempfile-XXXXXX";
+ int fd = mkstemp(filename.data());
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(access(filename.c_str(), F_OK), 0);
+
+ std::string actual_filename;
+ remove_file_at_fd(fd, &actual_filename);
+ EXPECT_NE(access(filename.c_str(), F_OK), 0);
+ EXPECT_EQ(filename, actual_filename);
+
+ close(fd);
}
} // namespace installd
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 3ce4b67..992425d 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -19,18 +19,21 @@
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
+#include <poll.h>
#include <stdlib.h>
#include <sys/capability.h>
+#include <sys/pidfd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/wait.h>
#include <sys/xattr.h>
+#include <unistd.h>
#include <uuid/uuid.h>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/strings.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/fs.h>
#include <cutils/properties.h>
@@ -195,39 +198,39 @@
}
/**
- * Create the path name where supplemental data for all apps will be stored.
- * E.g. /data/misc_ce/0/supplemental
+ * Create the path name where sdk_sandbox data for all apps will be stored.
+ * E.g. /data/misc_ce/0/sdksandbox
*/
-std::string create_data_misc_supplemental_path(const char* uuid, bool isCeData, userid_t user) {
+std::string create_data_misc_sdk_sandbox_path(const char* uuid, bool isCeData, userid_t user) {
std::string data(create_data_path(uuid));
if (isCeData) {
- return StringPrintf("%s/misc_ce/%d/supplemental", data.c_str(), user);
+ return StringPrintf("%s/misc_ce/%d/sdksandbox", data.c_str(), user);
} else {
- return StringPrintf("%s/misc_de/%d/supplemental", data.c_str(), user);
+ return StringPrintf("%s/misc_de/%d/sdksandbox", data.c_str(), user);
}
}
/**
* Create the path name where code data for all codes in a particular app will be stored.
- * E.g. /data/misc_ce/0/supplemental/<app-name>
+ * E.g. /data/misc_ce/0/sdksandbox/<app-name>
*/
-std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
- userid_t user, const char* package_name) {
+std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData,
+ userid_t user, const char* package_name) {
check_package_name(package_name);
return StringPrintf("%s/%s",
- create_data_misc_supplemental_path(volume_uuid, isCeData, user).c_str(),
+ create_data_misc_sdk_sandbox_path(volume_uuid, isCeData, user).c_str(),
package_name);
}
/**
* Create the path name where shared code data for a particular app will be stored.
- * E.g. /data/misc_ce/0/supplemental/<app-name>/shared
+ * E.g. /data/misc_ce/0/sdksandbox/<app-name>/shared
*/
-std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
- userid_t user, const char* package_name) {
+std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData,
+ userid_t user, const char* package_name) {
return StringPrintf("%s/shared",
- create_data_misc_supplemental_package_path(volume_uuid, isCeData, user,
- package_name)
+ create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user,
+ package_name)
.c_str());
}
@@ -1096,30 +1099,45 @@
return fs_prepare_dir(path.c_str(), 0750, uid, gid);
}
-int wait_child(pid_t pid)
-{
+static int wait_child(pid_t pid) {
int status;
- pid_t got_pid;
+ pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, /*options=*/0));
- while (1) {
- got_pid = waitpid(pid, &status, 0);
- if (got_pid == -1 && errno == EINTR) {
- printf("waitpid interrupted, retrying\n");
- } else {
- break;
- }
- }
if (got_pid != pid) {
- ALOGW("waitpid failed: wanted %d, got %d: %s\n",
- (int) pid, (int) got_pid, strerror(errno));
- return 1;
+ PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
+ return W_EXITCODE(/*exit_code=*/255, /*signal_number=*/0);
}
- if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
- return 0;
- } else {
- return status; /* always nonzero */
+ return status;
+}
+
+int wait_child_with_timeout(pid_t pid, int timeout_ms) {
+ int pidfd = pidfd_open(pid, /*flags=*/0);
+ if (pidfd < 0) {
+ PLOG(ERROR) << "pidfd_open failed for pid " << pid;
+ kill(pid, SIGKILL);
+ return wait_child(pid);
}
+
+ struct pollfd pfd;
+ pfd.fd = pidfd;
+ pfd.events = POLLIN;
+ int poll_ret = TEMP_FAILURE_RETRY(poll(&pfd, /*nfds=*/1, timeout_ms));
+
+ close(pidfd);
+
+ if (poll_ret < 0) {
+ PLOG(ERROR) << "poll failed for pid " << pid;
+ kill(pid, SIGKILL);
+ return wait_child(pid);
+ }
+ if (poll_ret == 0) {
+ LOG(WARNING) << "Child process " << pid << " timed out after " << timeout_ms
+ << "ms. Killing it";
+ kill(pid, SIGKILL);
+ return wait_child(pid);
+ }
+ return wait_child(pid);
}
/**
@@ -1332,5 +1350,27 @@
}
}
+bool remove_file_at_fd(int fd, /*out*/ std::string* path) {
+ char path_buffer[PATH_MAX + 1];
+ std::string proc_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+ ssize_t len = readlink(proc_path.c_str(), path_buffer, PATH_MAX);
+ if (len < 0) {
+ PLOG(WARNING) << "Could not remove file at fd " << fd << ": Failed to get file path";
+ return false;
+ }
+ path_buffer[len] = '\0';
+ if (path != nullptr) {
+ *path = path_buffer;
+ }
+ if (unlink(path_buffer) != 0) {
+ if (errno == ENOENT) {
+ return true;
+ }
+ PLOG(WARNING) << "Could not remove file at path " << path_buffer;
+ return false;
+ }
+ return true;
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 2db1623..4b56f99 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -60,12 +60,12 @@
std::string create_data_user_ce_package_path_as_user_link(
const char* volume_uuid, userid_t userid, const char* package_name);
-std::string create_data_misc_supplemental_path(const char* volume_uuid, bool isCeData,
- userid_t userid);
-std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
- userid_t userid, const char* package_name);
-std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
+std::string create_data_misc_sdk_sandbox_path(const char* volume_uuid, bool isCeData,
+ userid_t userid);
+std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData,
userid_t userid, const char* package_name);
+std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData,
+ userid_t userid, const char* package_name);
std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user);
std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user);
@@ -160,7 +160,8 @@
int ensure_config_user_dirs(userid_t userid);
-int wait_child(pid_t pid);
+// Waits for a child process, or kills it if it times out. Returns the exit code.
+int wait_child_with_timeout(pid_t pid, int timeout_ms);
int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
uid_t uid, gid_t gid);
@@ -177,6 +178,10 @@
void drop_capabilities(uid_t uid);
+// Removes a file specified by a file descriptor. Returns true on success. Reports the file path to
+// `path` if present.
+bool remove_file_at_fd(int fd, /*out*/ std::string* path = nullptr);
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp
index 60d6492..8c000a1 100644
--- a/cmds/installd/view_compiler.cpp
+++ b/cmds/installd/view_compiler.cpp
@@ -33,7 +33,13 @@
namespace android {
namespace installd {
-using base::unique_fd;
+namespace {
+
+using ::android::base::unique_fd;
+
+constexpr int kTimeoutMs = 300000;
+
+} // namespace
bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
int uid) {
@@ -88,7 +94,17 @@
_exit(1);
}
- return wait_child(pid) == 0;
+ int return_code = wait_child_with_timeout(pid, kTimeoutMs);
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "viewcompiler failed for " << package_name << ":" << apk_path;
+ if (WTERMSIG(return_code) == SIGKILL) {
+ // If the subprocess is killed while it's writing to the file, the file is likely
+ // corrupted, so we should remove it.
+ remove_file_at_fd(outfd.get());
+ }
+ return false;
+ }
+ return WEXITSTATUS(return_code) == 0;
}
} // namespace installd
diff --git a/include/input/Input.h b/include/input/Input.h
index 2837add..b23a951 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -814,6 +814,8 @@
static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
const PointerCoords&);
+ static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
+ const PointerCoords&);
protected:
int32_t mAction;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1821729..baa817c 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -19,6 +19,7 @@
#include <binder/ProcessState.h>
#include <android-base/result.h>
+#include <android-base/strings.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -367,8 +368,13 @@
String8 ProcessState::makeBinderThreadName() {
int32_t s = android_atomic_add(1, &mThreadPoolSeq);
pid_t pid = getpid();
+
+ std::string_view driverName = mDriverName.c_str();
+ android::base::ConsumePrefix(&driverName, "/dev/");
+
String8 name;
- name.appendFormat("%d_%X:%s", pid, s, mDriverName.c_str());
+ name.appendFormat("%.*s:%d_%X", static_cast<int>(driverName.length()), driverName.data(), pid,
+ s);
return name;
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 1d7de98..dbfb1c2 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -114,8 +114,8 @@
};
pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
pub use proxy::{
- get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder,
- WpIBinder,
+ get_declared_instances, get_interface, get_service, is_declared, wait_for_interface,
+ wait_for_service, DeathRecipient, SpIBinder, WpIBinder,
};
pub use state::{ProcessState, ThreadState};
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 12bfde7..e3e4730 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -28,9 +28,10 @@
use std::cmp::Ordering;
use std::convert::TryInto;
-use std::ffi::{c_void, CString};
+use std::ffi::{c_void, CStr, CString};
use std::fmt;
use std::mem;
+use std::os::raw::c_char;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::Arc;
@@ -778,6 +779,61 @@
}
}
+/// Check if a service is declared (e.g. in a VINTF manifest)
+pub fn is_declared(interface: &str) -> Result<bool> {
+ let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
+
+ unsafe {
+ // Safety: `interface` is a valid null-terminated C-style string and is
+ // only borrowed for the lifetime of the call. The `interface` local
+ // outlives this call as it lives for the function scope.
+ Ok(sys::AServiceManager_isDeclared(interface.as_ptr()))
+ }
+}
+
+/// Retrieve all declared instances for a particular interface
+///
+/// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo'
+/// is passed here, then ["foo"] would be returned.
+pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> {
+ unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) {
+ // Safety: opaque was a mutable pointer created below from a Vec of
+ // CString, and outlives this callback. The null handling here is just
+ // to avoid the possibility of unwinding across C code if this crate is
+ // ever compiled with panic=unwind.
+ if let Some(instances) = opaque.cast::<Vec<CString>>().as_mut() {
+ // Safety: instance is a valid null-terminated C string with a
+ // lifetime at least as long as this function, and we immediately
+ // copy it into an owned CString.
+ instances.push(CStr::from_ptr(instance).to_owned());
+ } else {
+ eprintln!("Opaque pointer was null in get_declared_instances callback!");
+ }
+ }
+
+ let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
+ let mut instances: Vec<CString> = vec![];
+ unsafe {
+ // Safety: `interface` and `instances` are borrowed for the length of
+ // this call and both outlive the call. `interface` is guaranteed to be
+ // a valid null-terminated C-style string.
+ sys::AServiceManager_forEachDeclaredInstance(
+ interface.as_ptr(),
+ &mut instances as *mut _ as *mut c_void,
+ Some(callback),
+ );
+ }
+
+ instances
+ .into_iter()
+ .map(CString::into_string)
+ .collect::<std::result::Result<Vec<String>, _>>()
+ .map_err(|e| {
+ eprintln!("An interface instance name was not a valid UTF-8 string: {}", e);
+ StatusCode::BAD_VALUE
+ })
+}
+
/// # Safety
///
/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 50daf1c..7c5afde 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -484,6 +484,21 @@
}
#[test]
+ fn get_declared_instances() {
+ // At the time of writing this test, there is no good VINTF interface
+ // guaranteed to be on all devices. Cuttlefish has light, so this will
+ // generally test things.
+ let has_lights = binder::is_declared("android.hardware.light.ILights/default")
+ .expect("Could not check for declared interface");
+
+ let instances = binder::get_declared_instances("android.hardware.light.ILights")
+ .expect("Could not get declared instances");
+
+ let expected_defaults = if has_lights { 1 } else { 0 };
+ assert_eq!(expected_defaults, instances.iter().filter(|i| i.as_str() == "default").count());
+ }
+
+ #[test]
fn trivial_client() {
let service_name = "trivial_client_test";
let _process = ScopedServiceProcess::new(service_name);
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 7ce72ff..b7a7aa0 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,12 +132,13 @@
}
}
-BLASTBufferQueue::BLASTBufferQueue(const std::string& name)
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
: mSurfaceControl(nullptr),
mSize(1, 1),
mRequestedSize(mSize),
mFormat(PIXEL_FORMAT_RGBA_8888),
- mSyncTransaction(nullptr) {
+ mSyncTransaction(nullptr),
+ mUpdateDestinationFrame(updateDestinationFrame) {
createBufferQueue(&mProducer, &mConsumer);
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
@@ -184,7 +185,7 @@
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
- int32_t format, SurfaceComposerClient::Transaction* outTransaction) {
+ int32_t format) {
LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
std::unique_lock _lock{mMutex};
@@ -193,7 +194,6 @@
mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
}
- SurfaceComposerClient::Transaction t;
const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
if (surfaceControlChanged && mSurfaceControl != nullptr) {
BQA_LOGD("Updating SurfaceControl without recreating BBQ");
@@ -203,6 +203,7 @@
// Always update the native object even though they might have the same layer handle, so we can
// get the updated transform hint from WM.
mSurfaceControl = surface;
+ SurfaceComposerClient::Transaction t;
if (surfaceControlChanged) {
t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
layer_state_t::eEnableBackpressure);
@@ -221,12 +222,10 @@
// If the buffer supports scaling, update the frame immediately since the client may
// want to scale the existing buffer to the new size.
mSize = mRequestedSize;
- SurfaceComposerClient::Transaction* destFrameTransaction =
- (outTransaction) ? outTransaction : &t;
- destFrameTransaction->setDestinationFrame(mSurfaceControl,
- Rect(0, 0, newSize.getWidth(),
- newSize.getHeight()));
- applyTransaction = true;
+ if (mUpdateDestinationFrame) {
+ t.setDestinationFrame(mSurfaceControl, Rect(newSize));
+ applyTransaction = true;
+ }
}
}
if (applyTransaction) {
@@ -498,7 +497,6 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- const bool updateDestinationFrame = mRequestedSize != mSize;
mSize = mRequestedSize;
Rect crop = computeCrop(bufferItem);
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
@@ -517,12 +515,19 @@
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
- if (updateDestinationFrame) {
- t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+ if (mUpdateDestinationFrame) {
+ t->setDestinationFrame(mSurfaceControl, Rect(mSize));
+ } else {
+ const bool ignoreDestinationFrame =
+ bufferItem.mScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE;
+ t->setFlags(mSurfaceControl,
+ ignoreDestinationFrame ? layer_state_t::eIgnoreDestinationFrame : 0,
+ layer_state_t::eIgnoreDestinationFrame);
}
t->setBufferCrop(mSurfaceControl, crop);
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
+ t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
if (!bufferItem.mIsAutoTimestamp) {
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
@@ -532,10 +537,6 @@
mNextFrameTimelineInfoQueue.pop();
}
- if (mAutoRefresh != bufferItem.mAutoRefresh) {
- t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
- mAutoRefresh = bufferItem.mAutoRefresh;
- }
{
std::unique_lock _lock{mTimestampMutex};
auto dequeueTime = mDequeueTimestamps.find(buffer->getId());
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 75c5e26..5ab0abc 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -46,9 +46,11 @@
namespace android {
+using gui::DisplayCaptureArgs;
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
using gui::IWindowInfosListener;
+using gui::LayerCaptureArgs;
using ui::ColorMode;
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
@@ -118,36 +120,6 @@
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
- status_t captureDisplay(const DisplayCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) override {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(args.write, data);
- SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
-
- return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
- }
-
- status_t captureDisplay(DisplayId displayId,
- const sp<IScreenCaptureListener>& captureListener) override {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(data.writeUint64, displayId.value);
- SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
-
- return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
- }
-
- status_t captureLayers(const LayerCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) override {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(args.write, data);
- SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
-
- return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
- }
-
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const override {
Parcel data, reply;
@@ -1451,36 +1423,6 @@
bootFinished();
return NO_ERROR;
}
- case CAPTURE_DISPLAY: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- DisplayCaptureArgs args;
- sp<IScreenCaptureListener> captureListener;
- SAFE_PARCEL(args.read, data);
- SAFE_PARCEL(data.readStrongBinder, &captureListener);
-
- return captureDisplay(args, captureListener);
- }
- case CAPTURE_DISPLAY_BY_ID: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- uint64_t value;
- SAFE_PARCEL(data.readUint64, &value);
- const auto id = DisplayId::fromValue(value);
- if (!id) return BAD_VALUE;
-
- sp<IScreenCaptureListener> captureListener;
- SAFE_PARCEL(data.readStrongBinder, &captureListener);
-
- return captureDisplay(*id, captureListener);
- }
- case CAPTURE_LAYERS: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- LayerCaptureArgs args;
- sp<IScreenCaptureListener> captureListener;
- SAFE_PARCEL(args.read, data);
- SAFE_PARCEL(data.readStrongBinder, &captureListener);
-
- return captureLayers(args, captureListener);
- }
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IGraphicBufferProducer> bufferProducer =
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9022e7d..6944d38 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -686,85 +686,89 @@
// ----------------------------------------------------------------------------
-status_t CaptureArgs::write(Parcel& output) const {
- SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(pixelFormat));
- SAFE_PARCEL(output.write, sourceCrop);
- SAFE_PARCEL(output.writeFloat, frameScaleX);
- SAFE_PARCEL(output.writeFloat, frameScaleY);
- SAFE_PARCEL(output.writeBool, captureSecureLayers);
- SAFE_PARCEL(output.writeInt32, uid);
- SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace));
- SAFE_PARCEL(output.writeBool, allowProtected);
- SAFE_PARCEL(output.writeBool, grayscale);
+namespace gui {
+
+status_t CaptureArgs::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(pixelFormat));
+ SAFE_PARCEL(output->write, sourceCrop);
+ SAFE_PARCEL(output->writeFloat, frameScaleX);
+ SAFE_PARCEL(output->writeFloat, frameScaleY);
+ SAFE_PARCEL(output->writeBool, captureSecureLayers);
+ SAFE_PARCEL(output->writeInt32, uid);
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(dataspace));
+ SAFE_PARCEL(output->writeBool, allowProtected);
+ SAFE_PARCEL(output->writeBool, grayscale);
return NO_ERROR;
}
-status_t CaptureArgs::read(const Parcel& input) {
+status_t CaptureArgs::readFromParcel(const Parcel* input) {
int32_t value = 0;
- SAFE_PARCEL(input.readInt32, &value);
+ SAFE_PARCEL(input->readInt32, &value);
pixelFormat = static_cast<ui::PixelFormat>(value);
- SAFE_PARCEL(input.read, sourceCrop);
- SAFE_PARCEL(input.readFloat, &frameScaleX);
- SAFE_PARCEL(input.readFloat, &frameScaleY);
- SAFE_PARCEL(input.readBool, &captureSecureLayers);
- SAFE_PARCEL(input.readInt32, &uid);
- SAFE_PARCEL(input.readInt32, &value);
+ SAFE_PARCEL(input->read, sourceCrop);
+ SAFE_PARCEL(input->readFloat, &frameScaleX);
+ SAFE_PARCEL(input->readFloat, &frameScaleY);
+ SAFE_PARCEL(input->readBool, &captureSecureLayers);
+ SAFE_PARCEL(input->readInt32, &uid);
+ SAFE_PARCEL(input->readInt32, &value);
dataspace = static_cast<ui::Dataspace>(value);
- SAFE_PARCEL(input.readBool, &allowProtected);
- SAFE_PARCEL(input.readBool, &grayscale);
+ SAFE_PARCEL(input->readBool, &allowProtected);
+ SAFE_PARCEL(input->readBool, &grayscale);
return NO_ERROR;
}
-status_t DisplayCaptureArgs::write(Parcel& output) const {
- SAFE_PARCEL(CaptureArgs::write, output);
+status_t DisplayCaptureArgs::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(CaptureArgs::writeToParcel, output);
- SAFE_PARCEL(output.writeStrongBinder, displayToken);
- SAFE_PARCEL(output.writeUint32, width);
- SAFE_PARCEL(output.writeUint32, height);
- SAFE_PARCEL(output.writeBool, useIdentityTransform);
+ SAFE_PARCEL(output->writeStrongBinder, displayToken);
+ SAFE_PARCEL(output->writeUint32, width);
+ SAFE_PARCEL(output->writeUint32, height);
+ SAFE_PARCEL(output->writeBool, useIdentityTransform);
return NO_ERROR;
}
-status_t DisplayCaptureArgs::read(const Parcel& input) {
- SAFE_PARCEL(CaptureArgs::read, input);
+status_t DisplayCaptureArgs::readFromParcel(const Parcel* input) {
+ SAFE_PARCEL(CaptureArgs::readFromParcel, input);
- SAFE_PARCEL(input.readStrongBinder, &displayToken);
- SAFE_PARCEL(input.readUint32, &width);
- SAFE_PARCEL(input.readUint32, &height);
- SAFE_PARCEL(input.readBool, &useIdentityTransform);
+ SAFE_PARCEL(input->readStrongBinder, &displayToken);
+ SAFE_PARCEL(input->readUint32, &width);
+ SAFE_PARCEL(input->readUint32, &height);
+ SAFE_PARCEL(input->readBool, &useIdentityTransform);
return NO_ERROR;
}
-status_t LayerCaptureArgs::write(Parcel& output) const {
- SAFE_PARCEL(CaptureArgs::write, output);
+status_t LayerCaptureArgs::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(CaptureArgs::writeToParcel, output);
- SAFE_PARCEL(output.writeStrongBinder, layerHandle);
- SAFE_PARCEL(output.writeInt32, excludeHandles.size());
+ SAFE_PARCEL(output->writeStrongBinder, layerHandle);
+ SAFE_PARCEL(output->writeInt32, excludeHandles.size());
for (auto el : excludeHandles) {
- SAFE_PARCEL(output.writeStrongBinder, el);
+ SAFE_PARCEL(output->writeStrongBinder, el);
}
- SAFE_PARCEL(output.writeBool, childrenOnly);
+ SAFE_PARCEL(output->writeBool, childrenOnly);
return NO_ERROR;
}
-status_t LayerCaptureArgs::read(const Parcel& input) {
- SAFE_PARCEL(CaptureArgs::read, input);
+status_t LayerCaptureArgs::readFromParcel(const Parcel* input) {
+ SAFE_PARCEL(CaptureArgs::readFromParcel, input);
- SAFE_PARCEL(input.readStrongBinder, &layerHandle);
+ SAFE_PARCEL(input->readStrongBinder, &layerHandle);
int32_t numExcludeHandles = 0;
- SAFE_PARCEL_READ_SIZE(input.readInt32, &numExcludeHandles, input.dataSize());
+ SAFE_PARCEL_READ_SIZE(input->readInt32, &numExcludeHandles, input->dataSize());
excludeHandles.reserve(numExcludeHandles);
for (int i = 0; i < numExcludeHandles; i++) {
sp<IBinder> binder;
- SAFE_PARCEL(input.readStrongBinder, &binder);
+ SAFE_PARCEL(input->readStrongBinder, &binder);
excludeHandles.emplace(binder);
}
- SAFE_PARCEL(input.readBool, &childrenOnly);
+ SAFE_PARCEL(input->readBool, &childrenOnly);
return NO_ERROR;
}
+}; // namespace gui
+
ReleaseCallbackId BufferData::generateReleaseCallbackId() const {
return {buffer->getId(), frameNumber};
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9269c3e..26ccda5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -46,6 +46,7 @@
#include <ui/DynamicDisplayInfo.h>
#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
// This server size should always be smaller than the server cache size
#define BUFFER_CACHE_MAX_SIZE 64
@@ -62,6 +63,7 @@
// ---------------------------------------------------------------------------
ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
+ANDROID_SINGLETON_STATIC_INSTANCE(ComposerServiceAIDL);
namespace {
// Initialize transaction id counter used to generate transaction ids
@@ -120,6 +122,52 @@
mDeathObserver = nullptr;
}
+ComposerServiceAIDL::ComposerServiceAIDL() : Singleton<ComposerServiceAIDL>() {
+ std::scoped_lock lock(mMutex);
+ connectLocked();
+}
+
+bool ComposerServiceAIDL::connectLocked() {
+ const String16 name("SurfaceFlingerAIDL");
+ mComposerService = waitForService<gui::ISurfaceComposer>(name);
+ if (mComposerService == nullptr) {
+ return false; // fatal error or permission problem
+ }
+
+ // Create the death listener.
+ class DeathObserver : public IBinder::DeathRecipient {
+ ComposerServiceAIDL& mComposerService;
+ virtual void binderDied(const wp<IBinder>& who) {
+ ALOGW("ComposerService aidl remote (surfaceflinger) died [%p]", who.unsafe_get());
+ mComposerService.composerServiceDied();
+ }
+
+ public:
+ explicit DeathObserver(ComposerServiceAIDL& mgr) : mComposerService(mgr) {}
+ };
+
+ mDeathObserver = new DeathObserver(*const_cast<ComposerServiceAIDL*>(this));
+ IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
+ return true;
+}
+
+/*static*/ sp<gui::ISurfaceComposer> ComposerServiceAIDL::getComposerService() {
+ ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
+ std::scoped_lock lock(instance.mMutex);
+ if (instance.mComposerService == nullptr) {
+ if (ComposerServiceAIDL::getInstance().connectLocked()) {
+ ALOGD("ComposerServiceAIDL reconnected");
+ }
+ }
+ return instance.mComposerService;
+}
+
+void ComposerServiceAIDL::composerServiceDied() {
+ std::scoped_lock lock(mMutex);
+ mComposerService = nullptr;
+ mDeathObserver = nullptr;
+}
+
class DefaultComposerClient: public Singleton<DefaultComposerClient> {
Mutex mLock;
sp<SurfaceComposerClient> mClient;
@@ -2267,26 +2315,29 @@
status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
const sp<IScreenCaptureListener>& captureListener) {
- sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureDisplay(captureArgs, captureListener);
+ binder::Status status = s->captureDisplay(captureArgs, captureListener);
+ return status.transactionError();
}
status_t ScreenshotClient::captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) {
- sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureDisplay(displayId, captureListener);
+ binder::Status status = s->captureDisplayById(displayId.value, captureListener);
+ return status.transactionError();
}
status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
const sp<IScreenCaptureListener>& captureListener) {
- sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureLayers(captureArgs, captureListener);
+ binder::Status status = s->captureLayers(captureArgs, captureListener);
+ return status.transactionError();
}
// ---------------------------------------------------------------------------------
diff --git a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
new file mode 100644
index 0000000..2caa2b9
--- /dev/null
+++ b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h";
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
new file mode 100644
index 0000000..07921a5
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -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.
+ */
+
+package android.gui;
+
+import android.gui.DisplayCaptureArgs;
+import android.gui.LayerCaptureArgs;
+import android.gui.IScreenCaptureListener;
+
+/** @hide */
+interface ISurfaceComposer {
+ /**
+ * Capture the specified screen. This requires READ_FRAME_BUFFER
+ * permission. This function will fail if there is a secure window on
+ * screen and DisplayCaptureArgs.captureSecureLayers is false.
+ *
+ * This function can capture a subregion (the source crop) of the screen.
+ * The subregion can be optionally rotated. It will also be scaled to
+ * match the size of the output buffer.
+ */
+ void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener);
+ void captureDisplayById(long displayId, IScreenCaptureListener listener);
+ /**
+ * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
+ * This requires READ_FRAME_BUFFER permission. This function will fail if there
+ * is a secure window on screen
+ */
+ void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener);
+}
diff --git a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
new file mode 100644
index 0000000..f0def50
--- /dev/null
+++ b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h";
diff --git a/libs/gui/android/gui/FocusRequest.aidl b/libs/gui/android/gui/FocusRequest.aidl
index 9018635..b13c600 100644
--- a/libs/gui/android/gui/FocusRequest.aidl
+++ b/libs/gui/android/gui/FocusRequest.aidl
@@ -21,7 +21,7 @@
/**
* Input channel token used to identify the window that should gain focus.
*/
- IBinder token;
+ @nullable IBinder token;
@utf8InCpp String windowName;
/**
* The token that the caller expects currently to be focused. If the
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 1ed592b..74addea 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -73,7 +73,7 @@
: public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
{
public:
- BLASTBufferQueue(const std::string& name);
+ BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
int height, int32_t format);
@@ -100,8 +100,7 @@
void applyPendingTransactions(uint64_t frameNumber);
SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber);
- void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
- SurfaceComposerClient::Transaction* outTransaction = nullptr);
+ void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -218,11 +217,6 @@
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
- // Last requested auto refresh state set by the producer. The state indicates that the consumer
- // should acquire the next frame as soon as it can and not wait for a frame to become available.
- // This is only relevant for shared buffer mode.
- bool mAutoRefresh GUARDED_BY(mMutex) = false;
-
std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
// Tracks the last acquired frame number
@@ -250,6 +244,12 @@
// Flag to determine if syncTransaction should only acquire a single buffer and then clear or
// continue to acquire buffers until explicitly cleared
bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true;
+
+ // True if BBQ will update the destination frame used to scale the buffer to the requested size.
+ // If false, the caller is responsible for updating the destination frame on the BBQ
+ // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with
+ // additional scales in the hierarchy.
+ bool mUpdateDestinationFrame GUARDED_BY(mMutex) = true;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
new file mode 100644
index 0000000..ec884cf
--- /dev/null
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
+
+namespace android::gui {
+
+struct CaptureArgs : public Parcelable {
+ const static int32_t UNSET_UID = -1;
+ virtual ~CaptureArgs() = default;
+
+ ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
+ Rect sourceCrop;
+ float frameScaleX{1};
+ float frameScaleY{1};
+ bool captureSecureLayers{false};
+ int32_t uid{UNSET_UID};
+ // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
+ // result will be in the display's colorspace.
+ // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+ // different from SRGB (byte per color), and failed when checking colors in tests.
+ // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+
+ // The receiver of the capture can handle protected buffer. A protected buffer has
+ // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
+ // Any read/write access from unprotected context will result in undefined behaviour.
+ // Protected contents are typically DRM contents. This has no direct implication to the
+ // secure property of the surface, which is specified by the application explicitly to avoid
+ // the contents being accessed/captured by screenshot or unsecure display.
+ bool allowProtected = false;
+
+ bool grayscale = false;
+
+ virtual status_t writeToParcel(Parcel* output) const;
+ virtual status_t readFromParcel(const Parcel* input);
+};
+
+struct DisplayCaptureArgs : CaptureArgs {
+ sp<IBinder> displayToken;
+ uint32_t width{0};
+ uint32_t height{0};
+ bool useIdentityTransform{false};
+
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+};
+
+}; // namespace android::gui
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 0a59f52..4dfc383 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -58,11 +58,9 @@
struct client_cache_t;
struct ComposerState;
-struct DisplayCaptureArgs;
struct DisplayStatInfo;
struct DisplayState;
struct InputWindowCommands;
-struct LayerCaptureArgs;
class LayerDebugInfo;
class HdrCapabilities;
class IGraphicBufferProducer;
@@ -75,6 +73,13 @@
using gui::IScreenCaptureListener;
using gui::SpHash;
+namespace gui {
+
+struct DisplayCaptureArgs;
+struct LayerCaptureArgs;
+
+} // namespace gui
+
namespace ui {
struct DisplayMode;
@@ -261,27 +266,6 @@
*/
virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
- /**
- * Capture the specified screen. This requires READ_FRAME_BUFFER
- * permission. This function will fail if there is a secure window on
- * screen and DisplayCaptureArgs.captureSecureLayers is false.
- *
- * This function can capture a subregion (the source crop) of the screen.
- * The subregion can be optionally rotated. It will also be scaled to
- * match the size of the output buffer.
- */
- virtual status_t captureDisplay(const DisplayCaptureArgs&,
- const sp<IScreenCaptureListener>&) = 0;
-
- virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;
-
- /**
- * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
- * This requires READ_FRAME_BUFFER permission. This function will fail if there
- * is a secure window on screen
- */
- virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;
-
/* Clears the frame statistics for animations.
*
* Requires the ACCESS_SURFACE_FLINGER permission.
@@ -621,8 +605,8 @@
GET_DISPLAY_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
GET_ACTIVE_DISPLAY_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
GET_DISPLAY_STATE,
- CAPTURE_DISPLAY,
- CAPTURE_LAYERS,
+ CAPTURE_DISPLAY, // Deprecated. Autogenerated by .aidl now.
+ CAPTURE_LAYERS, // Deprecated. Autogenerated by .aidl now.
CLEAR_ANIMATION_FRAME_STATS,
GET_ANIMATION_FRAME_STATS,
SET_POWER_MODE,
@@ -649,7 +633,7 @@
GET_DESIRED_DISPLAY_MODE_SPECS,
GET_DISPLAY_BRIGHTNESS_SUPPORT,
SET_DISPLAY_BRIGHTNESS,
- CAPTURE_DISPLAY_BY_ID,
+ CAPTURE_DISPLAY_BY_ID, // Deprecated. Autogenerated by .aidl now.
NOTIFY_POWER_BOOST,
SET_GLOBAL_SHADOW_SETTINGS,
GET_AUTO_LOW_LATENCY_MODE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
diff --git a/libs/gui/include/gui/LayerCaptureArgs.h b/libs/gui/include/gui/LayerCaptureArgs.h
new file mode 100644
index 0000000..05ff9d5
--- /dev/null
+++ b/libs/gui/include/gui/LayerCaptureArgs.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/DisplayCaptureArgs.h>
+#include <gui/SpHash.h>
+#include <unordered_set>
+
+namespace android::gui {
+
+struct LayerCaptureArgs : CaptureArgs {
+ sp<IBinder> layerHandle;
+ std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
+ bool childrenOnly{false};
+
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+};
+
+}; // namespace android::gui
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f720619..885b4ae 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -29,7 +29,9 @@
#include <android/gui/DropInputMode.h>
#include <android/gui/FocusRequest.h>
+#include <gui/DisplayCaptureArgs.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
@@ -128,8 +130,13 @@
// Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is
// set. This blocks the client until all the buffers have been presented. If the buffers
// have presentation timestamps, then we may drop buffers.
- eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE
- eLayerIsDisplayDecoration = 0x200, // DISPLAY_DECORATION
+ eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE
+ eLayerIsDisplayDecoration = 0x200, // DISPLAY_DECORATION
+ // Ignore any destination frame set on the layer. This is used when the buffer scaling mode
+ // is freeze and the destination frame is applied asynchronously with the buffer submission.
+ // This is needed to maintain compatibility for SurfaceView scaling behavior.
+ // See SurfaceView scaling behavior for more details.
+ eIgnoreDestinationFrame = 0x400,
};
enum {
@@ -370,56 +377,6 @@
bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
const char* functionName, bool privileged = false);
-struct CaptureArgs {
- const static int32_t UNSET_UID = -1;
- virtual ~CaptureArgs() = default;
-
- ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
- Rect sourceCrop;
- float frameScaleX{1};
- float frameScaleY{1};
- bool captureSecureLayers{false};
- int32_t uid{UNSET_UID};
- // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
- // result will be in the display's colorspace.
- // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
- // different from SRGB (byte per color), and failed when checking colors in tests.
- // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
- ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
-
- // The receiver of the capture can handle protected buffer. A protected buffer has
- // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
- // Any read/write access from unprotected context will result in undefined behaviour.
- // Protected contents are typically DRM contents. This has no direct implication to the
- // secure property of the surface, which is specified by the application explicitly to avoid
- // the contents being accessed/captured by screenshot or unsecure display.
- bool allowProtected = false;
-
- bool grayscale = false;
-
- virtual status_t write(Parcel& output) const;
- virtual status_t read(const Parcel& input);
-};
-
-struct DisplayCaptureArgs : CaptureArgs {
- sp<IBinder> displayToken;
- uint32_t width{0};
- uint32_t height{0};
- bool useIdentityTransform{false};
-
- status_t write(Parcel& output) const override;
- status_t read(const Parcel& input) override;
-};
-
-struct LayerCaptureArgs : CaptureArgs {
- sp<IBinder> layerHandle;
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
- bool childrenOnly{false};
-
- status_t write(Parcel& output) const override;
- status_t read(const Parcel& input) override;
-};
-
}; // namespace android
#endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 25637ef..6c79b5b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -57,7 +57,9 @@
class ITunnelModeEnabledListener;
class Region;
+using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
+using gui::LayerCaptureArgs;
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
diff --git a/libs/gui/include/private/gui/ComposerService.h b/libs/gui/include/private/gui/ComposerService.h
index fa1071a..05ed0a0 100644
--- a/libs/gui/include/private/gui/ComposerService.h
+++ b/libs/gui/include/private/gui/ComposerService.h
@@ -37,7 +37,7 @@
// Users of this class should not retain the value from
// getComposerService() for an extended period.
//
-// (It's not clear that using Singleton is useful here anymore.)
+// (TODO: b/219785927, It's not clear that using Singleton is useful here anymore.)
class ComposerService : public Singleton<ComposerService>
{
sp<ISurfaceComposer> mComposerService;
diff --git a/libs/gui/include/private/gui/ComposerServiceAIDL.h b/libs/gui/include/private/gui/ComposerServiceAIDL.h
new file mode 100644
index 0000000..fee37ee
--- /dev/null
+++ b/libs/gui/include/private/gui/ComposerServiceAIDL.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android/gui/ISurfaceComposer.h>
+
+#include <utils/Singleton.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+
+// This holds our connection to the composer service (i.e. SurfaceFlinger).
+// If the remote side goes away, we will re-establish the connection.
+// Users of this class should not retain the value from
+// getComposerService() for an extended period.
+//
+// (TODO: b/219785927, It's not clear that using Singleton is useful here anymore.)
+class ComposerServiceAIDL : public Singleton<ComposerServiceAIDL> {
+ sp<gui::ISurfaceComposer> mComposerService;
+ sp<IBinder::DeathRecipient> mDeathObserver;
+ mutable std::mutex mMutex;
+
+ ComposerServiceAIDL();
+ bool connectLocked();
+ void composerServiceDied();
+ friend class Singleton<ComposerServiceAIDL>;
+
+public:
+ // Get a connection to the Composer Service. This will block until
+ // a connection is established. Returns null if permission is denied.
+ static sp<gui::ISurfaceComposer> getComposerService();
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 42a32f3..179bdd7 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -29,6 +29,7 @@
#include <gui/SyncScreenCaptureListener.h>
#include <gui/test/CallbackUtils.h>
#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
#include <ui/DisplayMode.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
@@ -283,13 +284,13 @@
static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
ScreenCaptureResults& captureResults) {
- const auto sf = ComposerService::getComposerService();
+ const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t status = sf->captureDisplay(captureArgs, captureListener);
- if (status != NO_ERROR) {
- return status;
+ binder::Status status = sf->captureDisplay(captureArgs, captureListener);
+ if (status.transactionError() != NO_ERROR) {
+ return status.transactionError();
}
captureResults = captureListener->waitForResults();
return captureResults.result;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 6056280..a885e92 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -31,6 +31,7 @@
#include <gui/SyncScreenCaptureListener.h>
#include <inttypes.h>
#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
#include <sys/types.h>
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
@@ -205,13 +206,13 @@
static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
ScreenCaptureResults& captureResults) {
- const auto sf = ComposerService::getComposerService();
+ const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t status = sf->captureDisplay(captureArgs, captureListener);
- if (status != NO_ERROR) {
- return status;
+ binder::Status status = sf->captureDisplay(captureArgs, captureListener);
+ if (status.transactionError() != NO_ERROR) {
+ return status.transactionError();
}
captureResults = captureListener->waitForResults();
return captureResults.result;
@@ -766,16 +767,6 @@
void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {
- return NO_ERROR;
- }
- status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {
- return NO_ERROR;
- }
- status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override {
- return NO_ERROR;
- }
-
status_t clearAnimationFrameStats() override { return NO_ERROR; }
status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
return NO_ERROR;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 3073d94..fe1754c 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -846,6 +846,7 @@
return calculateTransformedXYUnchecked(source, transform, xy);
}
+// Keep in sync with calculateTransformedCoords.
float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
const ui::Transform& transform,
const PointerCoords& coords) {
@@ -874,6 +875,34 @@
return coords.getAxisValue(axis);
}
+// Keep in sync with calculateTransformedAxisValue. This is an optimization of
+// calculateTransformedAxisValue for all PointerCoords axes.
+PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source,
+ const ui::Transform& transform,
+ const PointerCoords& coords) {
+ if (shouldDisregardTransformation(source)) {
+ return coords;
+ }
+ PointerCoords out = coords;
+
+ const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
+ out.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ out.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ const vec2 relativeXy =
+ transformWithoutTranslation(transform,
+ {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
+
+ out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ transformAngle(transform,
+ coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)));
+
+ return out;
+}
+
// --- FocusEvent ---
void FocusEvent::initialize(int32_t id, bool hasFocus) {
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index cd20a64..9691ad8 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -61,18 +61,13 @@
ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
}
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken,
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
const std::string& reason) override {
ALOGE("Window is not responding: %s", reason.c_str());
}
- void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {}
-
- void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) override {
- ALOGE("Monitor is not responding: %s", reason.c_str());
- }
-
- void notifyMonitorResponsive(int32_t pid) override {}
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken,
+ std::optional<int32_t> pid) override {}
void notifyInputChannelBroken(const sp<IBinder>&) override {}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 58c9303..32c3a12 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4426,19 +4426,9 @@
const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
for (uint32_t i = 0; i < entry.pointerCount; i++) {
- PointerCoords& pc = entry.pointerCoords[i];
- // Make a copy of the injected coords. We cannot change them in place because some of them
- // are interdependent (for example, X coordinate might depend on the Y coordinate).
- PointerCoords injectedCoords = entry.pointerCoords[i];
-
- BitSet64 bits(injectedCoords.bits);
- while (!bits.isEmpty()) {
- const auto axis = static_cast<int32_t>(bits.clearFirstMarkedBit());
- const float value =
- MotionEvent::calculateTransformedAxisValue(axis, entry.source,
- transformToDisplay, injectedCoords);
- pc.setAxisValue(axis, value);
- }
+ entry.pointerCoords[i] =
+ MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
+ entry.pointerCoords[i]);
}
}
@@ -5816,35 +5806,21 @@
}
}
-void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
- auto command = [this, pid, reason = std::move(reason)]() REQUIRES(mLock) {
- scoped_unlock unlock(mLock);
- mPolicy->notifyMonitorUnresponsive(pid, reason);
- };
- postCommandLocked(std::move(command));
-}
-
void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
+ std::optional<int32_t> pid,
std::string reason) {
- auto command = [this, token, reason = std::move(reason)]() REQUIRES(mLock) {
+ auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyWindowUnresponsive(token, reason);
+ mPolicy->notifyWindowUnresponsive(token, pid, reason);
};
postCommandLocked(std::move(command));
}
-void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
- auto command = [this, pid]() REQUIRES(mLock) {
+void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& token,
+ std::optional<int32_t> pid) {
+ auto command = [this, token, pid]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyMonitorResponsive(pid);
- };
- postCommandLocked(std::move(command));
-}
-
-void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) {
- auto command = [this, connectionToken]() REQUIRES(mLock) {
- scoped_unlock unlock(mLock);
- mPolicy->notifyWindowResponsive(connectionToken);
+ mPolicy->notifyWindowResponsive(token, pid);
};
postCommandLocked(std::move(command));
}
@@ -5857,22 +5833,21 @@
void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
std::string reason) {
const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+ std::optional<int32_t> pid;
if (connection.monitor) {
ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
reason.c_str());
- std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
- if (!pid.has_value()) {
- ALOGE("Could not find unresponsive monitor for connection %s",
- connection.inputChannel->getName().c_str());
- return;
+ pid = findMonitorPidByTokenLocked(connectionToken);
+ } else {
+ // The connection is a window
+ ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+ reason.c_str());
+ const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);
+ if (handle != nullptr) {
+ pid = handle->getInfo()->ownerPid;
}
- sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason));
- return;
}
- // If not a monitor, must be a window
- ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
- reason.c_str());
- sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason));
+ sendWindowUnresponsiveCommandLocked(connectionToken, pid, std::move(reason));
}
/**
@@ -5880,18 +5855,17 @@
*/
void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) {
const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+ std::optional<int32_t> pid;
if (connection.monitor) {
- std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
- if (!pid.has_value()) {
- ALOGE("Could not find responsive monitor for connection %s",
- connection.inputChannel->getName().c_str());
- return;
+ pid = findMonitorPidByTokenLocked(connectionToken);
+ } else {
+ // The connection is a window
+ const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);
+ if (handle != nullptr) {
+ pid = handle->getInfo()->ownerPid;
}
- sendMonitorResponsiveCommandLocked(pid.value());
- return;
}
- // If not a monitor, must be a window
- sendWindowResponsiveCommandLocked(connectionToken);
+ sendWindowResponsiveCommandLocked(connectionToken, pid);
}
bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e162c78..d3e171a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -503,11 +503,11 @@
*/
void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
- void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
- void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken, std::string reason)
+ void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken,
+ std::optional<int32_t> pid, std::string reason)
REQUIRES(mLock);
- void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
- void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
+ void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken,
+ std::optional<int32_t> pid) REQUIRES(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 3c1e637..de0b6da 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -51,30 +51,19 @@
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
/* Notifies the system that a window just became unresponsive. This indicates that ANR
- * should be raised for this window. The window is identified via token.
- * The string reason contains information about the input event that we haven't received
- * a response for.
+ * should be raised for this window. The window can be identified via its input token and the
+ * pid of the owner. The string reason contains information about the input event that we
+ * haven't received a response for.
*/
- virtual void notifyWindowUnresponsive(const sp<IBinder>& token, const std::string& reason) = 0;
- /* Notifies the system that a monitor just became unresponsive. This indicates that ANR
- * should be raised for this monitor. The monitor is identified via its pid.
- * The string reason contains information about the input event that we haven't received
- * a response for.
- */
- virtual void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) = 0;
+ virtual void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<int32_t> pid,
+ const std::string& reason) = 0;
/* Notifies the system that a window just became responsive. This is only called after the
* window was first marked "unresponsive". This indicates that ANR dialog (if any) should
* no longer should be shown to the user. The window is eligible to cause a new ANR in the
* future.
*/
- virtual void notifyWindowResponsive(const sp<IBinder>& token) = 0;
- /* Notifies the system that a monitor just became responsive. This is only called after the
- * monitor was first marked "unresponsive". This indicates that ANR dialog (if any) should
- * no longer should be shown to the user. The monitor is eligible to cause a new ANR in the
- * future.
- */
- virtual void notifyMonitorResponsive(int32_t pid) = 0;
+ virtual void notifyWindowResponsive(const sp<IBinder>& token, std::optional<int32_t> pid) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index ffce9f6..91be4a3 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -84,6 +84,30 @@
ASSERT_FALSE(changes);
}
+TEST(FocusResolverTest, RemoveFocusFromFocusedWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ std::vector<sp<WindowInfoHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ // Focusable window gets focus.
+ request.token = focusableWindowToken;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, nullptr, focusableWindowToken);
+
+ // Window token of a request is null, focus should be revoked.
+ request.token = NULL;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, focusableWindowToken, nullptr);
+}
+
TEST(FocusResolverTest, SetFocusedMirroredWindow) {
sp<IBinder> focusableWindowToken = new BBinder();
sp<IBinder> invisibleWindowToken = new BBinder();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ae8358a..b3f51ee 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -88,6 +88,8 @@
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
InputDispatcherConfiguration mConfig;
+ using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>;
+
protected:
virtual ~FakeInputDispatcherPolicy() {}
@@ -161,122 +163,71 @@
void assertNotifyNoFocusedWindowAnrWasCalled(
std::chrono::nanoseconds timeout,
const std::shared_ptr<InputApplicationHandle>& expectedApplication) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
std::shared_ptr<InputApplicationHandle> application;
- { // acquire lock
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- ASSERT_NO_FATAL_FAILURE(
- application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
- } // release lock
+ ASSERT_NO_FATAL_FAILURE(
+ application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
ASSERT_EQ(expectedApplication, application);
}
void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
- const sp<IBinder>& expectedConnectionToken) {
- sp<IBinder> connectionToken = getUnresponsiveWindowToken(timeout);
- ASSERT_EQ(expectedConnectionToken, connectionToken);
+ const sp<WindowInfoHandle>& window) {
+ LOG_ALWAYS_FATAL_IF(window == nullptr, "window should not be null");
+ assertNotifyWindowUnresponsiveWasCalled(timeout, window->getToken(),
+ window->getInfo()->ownerPid);
}
- void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
- sp<IBinder> connectionToken = getResponsiveWindowToken();
- ASSERT_EQ(expectedConnectionToken, connectionToken);
+ void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+ const sp<IBinder>& expectedToken,
+ int32_t expectedPid) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ AnrResult result;
+ ASSERT_NO_FATAL_FAILURE(result =
+ getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock));
+ const auto& [token, pid] = result;
+ ASSERT_EQ(expectedToken, token);
+ ASSERT_EQ(expectedPid, pid);
}
- void assertNotifyMonitorUnresponsiveWasCalled(std::chrono::nanoseconds timeout) {
- int32_t pid = getUnresponsiveMonitorPid(timeout);
- ASSERT_EQ(MONITOR_PID, pid);
- }
-
- void assertNotifyMonitorResponsiveWasCalled() {
- int32_t pid = getResponsiveMonitorPid();
- ASSERT_EQ(MONITOR_PID, pid);
- }
-
+ /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLocked(mLock);
- return getAnrTokenLockedInterruptible(timeout, mAnrWindowTokens, lock);
+ AnrResult result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock);
+ const auto& [token, _] = result;
+ return token;
}
+ void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken,
+ int32_t expectedPid) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ AnrResult result;
+ ASSERT_NO_FATAL_FAILURE(
+ result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock));
+ const auto& [token, pid] = result;
+ ASSERT_EQ(expectedToken, token);
+ ASSERT_EQ(expectedPid, pid);
+ }
+
+ /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
sp<IBinder> getResponsiveWindowToken() {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLocked(mLock);
- return getAnrTokenLockedInterruptible(0s, mResponsiveWindowTokens, lock);
- }
-
- int32_t getUnresponsiveMonitorPid(std::chrono::nanoseconds timeout) {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- return getAnrTokenLockedInterruptible(timeout, mAnrMonitorPids, lock);
- }
-
- int32_t getResponsiveMonitorPid() {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- return getAnrTokenLockedInterruptible(0s, mResponsiveMonitorPids, lock);
- }
-
- // All three ANR-related callbacks behave the same way, so we use this generic function to wait
- // for a specific container to become non-empty. When the container is non-empty, return the
- // first entry from the container and erase it.
- template <class T>
- T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
- std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
- // If there is an ANR, Dispatcher won't be idle because there are still events
- // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
- // before checking if ANR was called.
- // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
- // to provide it some time to act. 100ms seems reasonable.
- std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
- const std::chrono::time_point start = std::chrono::steady_clock::now();
- std::optional<T> token =
- getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
- if (!token.has_value()) {
- ADD_FAILURE() << "Did not receive the ANR callback";
- return {};
- }
-
- const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- // Ensure that the ANR didn't get raised too early. We can't be too strict here because
- // the dispatcher started counting before this function was called
- if (std::chrono::abs(timeout - waited) > 100ms) {
- ADD_FAILURE() << "ANR was raised too early or too late. Expected "
- << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
- << "ms, but waited "
- << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
- << "ms instead";
- }
- return *token;
- }
-
- template <class T>
- std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
- std::queue<T>& storage,
- std::unique_lock<std::mutex>& lock,
- std::condition_variable& condition)
- REQUIRES(mLock) {
- condition.wait_for(lock, timeout,
- [&storage]() REQUIRES(mLock) { return !storage.empty(); });
- if (storage.empty()) {
- ADD_FAILURE() << "Did not receive the expected callback";
- return std::nullopt;
- }
- T item = storage.front();
- storage.pop();
- return std::make_optional(item);
+ AnrResult result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock);
+ const auto& [token, _] = result;
+ return token;
}
void assertNotifyAnrWasNotCalled() {
std::scoped_lock lock(mLock);
ASSERT_TRUE(mAnrApplications.empty());
- ASSERT_TRUE(mAnrWindowTokens.empty());
- ASSERT_TRUE(mAnrMonitorPids.empty());
- ASSERT_TRUE(mResponsiveWindowTokens.empty())
+ ASSERT_TRUE(mAnrWindows.empty());
+ ASSERT_TRUE(mResponsiveWindows.empty())
<< "ANR was not called, but please also consume the 'connection is responsive' "
"signal";
- ASSERT_TRUE(mResponsiveMonitorPids.empty())
- << "Monitor ANR was not called, but please also consume the 'monitor is responsive'"
- " signal";
}
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -344,10 +295,8 @@
// ANR handling
std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
- std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
- std::queue<sp<IBinder>> mResponsiveWindowTokens GUARDED_BY(mLock);
- std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
- std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
+ std::queue<AnrResult> mAnrWindows GUARDED_BY(mLock);
+ std::queue<AnrResult> mResponsiveWindows GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock);
std::condition_variable mNotifyInputChannelBroken;
@@ -355,32 +304,74 @@
sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
+ // All three ANR-related callbacks behave the same way, so we use this generic function to wait
+ // for a specific container to become non-empty. When the container is non-empty, return the
+ // first entry from the container and erase it.
+ template <class T>
+ T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
+ // If there is an ANR, Dispatcher won't be idle because there are still events
+ // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
+ // before checking if ANR was called.
+ // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
+ // to provide it some time to act. 100ms seems reasonable.
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::optional<T> token =
+ getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
+ if (!token.has_value()) {
+ ADD_FAILURE() << "Did not receive the ANR callback";
+ return {};
+ }
+
+ const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+ // Ensure that the ANR didn't get raised too early. We can't be too strict here because
+ // the dispatcher started counting before this function was called
+ if (std::chrono::abs(timeout - waited) > 100ms) {
+ ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+ << "ms, but waited "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+ << "ms instead";
+ }
+ return *token;
+ }
+
+ template <class T>
+ std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
+ std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock,
+ std::condition_variable& condition)
+ REQUIRES(mLock) {
+ condition.wait_for(lock, timeout,
+ [&storage]() REQUIRES(mLock) { return !storage.empty(); });
+ if (storage.empty()) {
+ ADD_FAILURE() << "Did not receive the expected callback";
+ return std::nullopt;
+ }
+ T item = storage.front();
+ storage.pop();
+ return std::make_optional(item);
+ }
+
void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
}
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, const std::string&) override {
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
+ const std::string&) override {
std::scoped_lock lock(mLock);
- mAnrWindowTokens.push(connectionToken);
+ ASSERT_TRUE(pid.has_value());
+ mAnrWindows.push({connectionToken, *pid});
mNotifyAnr.notify_all();
}
- void notifyMonitorUnresponsive(int32_t pid, const std::string&) override {
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken,
+ std::optional<int32_t> pid) override {
std::scoped_lock lock(mLock);
- mAnrMonitorPids.push(pid);
- mNotifyAnr.notify_all();
- }
-
- void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {
- std::scoped_lock lock(mLock);
- mResponsiveWindowTokens.push(connectionToken);
- mNotifyAnr.notify_all();
- }
-
- void notifyMonitorResponsive(int32_t pid) override {
- std::scoped_lock lock(mLock);
- mResponsiveMonitorPids.push(pid);
+ ASSERT_TRUE(pid.has_value());
+ mResponsiveWindows.push({connectionToken, *pid});
mNotifyAnr.notify_all();
}
@@ -1245,6 +1236,8 @@
mInfo.ownerUid = ownerUid;
}
+ int32_t getPid() const { return mInfo.ownerPid; }
+
void destroyReceiver() { mInputReceiver = nullptr; }
int getChannelFd() { return mInputReceiver->getChannelFd(); }
@@ -4364,13 +4357,13 @@
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
mWindow->finishEvent(*sequenceNum);
mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
}
// Send a key to the app and have the app not respond right away.
@@ -4380,7 +4373,7 @@
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
ASSERT_TRUE(mDispatcher->waitForIdle());
}
@@ -4485,7 +4478,7 @@
// We have now sent down and up. Let's consume first event and then ANR on the second.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
}
// A spy window can receive an ANR
@@ -4500,13 +4493,13 @@
std::optional<uint32_t> sequenceNum = spy->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = spy->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy);
spy->finishEvent(*sequenceNum);
spy->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
0 /*flags*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(spy->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(spy->getToken(), mWindow->getPid());
}
// If an app is not responding to a key event, spy windows should continue to receive
@@ -4521,7 +4514,7 @@
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
// New tap will go to the spy window, but not to the window
tapOnWindow();
@@ -4530,7 +4523,7 @@
mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mWindow->assertNoEvents();
spy->assertNoEvents();
}
@@ -4547,7 +4540,7 @@
mWindow->consumeMotionDown();
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
// New tap will go to the spy window, but not to the window
tapOnWindow();
@@ -4556,7 +4549,7 @@
mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mWindow->assertNoEvents();
spy->assertNoEvents();
}
@@ -4574,13 +4567,13 @@
const std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
- mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(30ms);
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(30ms, monitor.getToken(), MONITOR_PID);
monitor.finishEvent(*consumeSeq);
monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(monitor.getToken(), MONITOR_PID);
}
// If a window is unresponsive, then you get anr. if the window later catches up and starts to
@@ -4595,19 +4588,19 @@
mWindow->consumeMotionDown();
// Block on ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
mWindow->consumeMotionUp(); // Now the connection should be healthy again
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mWindow->assertNoEvents();
tapOnWindow();
mWindow->consumeMotionDown();
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
mWindow->consumeMotionUp();
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mFakePolicy->assertNotifyAnrWasNotCalled();
mWindow->assertNoEvents();
}
@@ -4620,7 +4613,7 @@
WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow);
std::this_thread::sleep_for(windowTimeout);
// 'notifyConnectionUnresponsive' should only be called once per connection
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -4630,7 +4623,7 @@
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
mWindow->assertNoEvents();
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -4792,7 +4785,7 @@
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
// Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
// sequence to make it consistent
mFocusedWindow->consumeMotionCancel();
@@ -4803,7 +4796,8 @@
mFocusedWindow->assertNoEvents();
mUnfocusedWindow->assertNoEvents();
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(),
+ mFocusedWindow->getPid());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -4817,8 +4811,9 @@
tapOnFocusedWindow();
// we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
- sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms);
- sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms);
+ sp<IBinder> anrConnectionToken1, anrConnectionToken2;
+ ASSERT_NO_FATAL_FAILURE(anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms));
+ ASSERT_NO_FATAL_FAILURE(anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms));
// We don't know which window will ANR first. But both of them should happen eventually.
ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
@@ -4833,8 +4828,9 @@
mFocusedWindow->consumeMotionUp();
mUnfocusedWindow->consumeMotionOutside();
- sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveWindowToken();
- sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveWindowToken();
+ sp<IBinder> responsiveToken1, responsiveToken2;
+ ASSERT_NO_FATAL_FAILURE(responsiveToken1 = mFakePolicy->getResponsiveWindowToken());
+ ASSERT_NO_FATAL_FAILURE(responsiveToken2 = mFakePolicy->getResponsiveWindowToken());
// Both applications should be marked as responsive, in any order
ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
@@ -4858,7 +4854,7 @@
ASSERT_TRUE(upEventSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
@@ -4879,7 +4875,8 @@
// The second tap did not go to the focused window
mFocusedWindow->assertNoEvents();
// Since all events are finished, connection should be deemed healthy again
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(),
+ mFocusedWindow->getPid());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -4991,7 +4988,7 @@
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
mUnfocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionDown();
@@ -5010,7 +5007,8 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
}
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(),
+ mFocusedWindow->getPid());
mUnfocusedWindow->assertNoEvents();
mFocusedWindow->assertNoEvents();
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index 86c8b0d..4d1de96 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -232,7 +232,7 @@
break;
}
default: {
- ALOGE("Invalid sensor additional info tag: %d", srcInfo.payload.getTag());
+ ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag());
}
}
break;
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index d9958f3..000a2cb 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -76,6 +76,7 @@
"libaidlcommonsupport",
"libcompositionengine",
"libframetimeline",
+ "libgui_aidl_static",
"libperfetto_client_experimental",
"librenderengine",
"libscheduler",
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 02e444d..e6a76e8 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -271,7 +271,8 @@
// Translate destination frame into scale and position. If a destination frame is not set, use the
// provided scale and position
bool BufferStateLayer::updateGeometry() {
- if (mDrawingState.destinationFrame.isEmpty()) {
+ if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
+ mDrawingState.destinationFrame.isEmpty()) {
// If destination frame is not set, use the requested transform set via
// BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
return assignTransform(&mDrawingState.transform, mRequestedTransform);
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index aefc014..9302b7b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -41,6 +41,10 @@
"libtonemap",
"libtrace_proto",
"libaidlcommonsupport",
+ "libprocessgroup",
+ "libcgrouprc",
+ "libjsoncpp",
+ "libcgrouprc_format",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -68,6 +72,7 @@
"src/DisplayColorProfile.cpp",
"src/DisplaySurface.cpp",
"src/DumpHelpers.cpp",
+ "src/HwcAsyncWorker.cpp",
"src/HwcBufferCache.cpp",
"src/LayerFECompositionState.cpp",
"src/Output.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 47aacc9..6a3fcb7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -57,7 +57,7 @@
virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
// Returns the boot display mode preferred by HWC.
- virtual int32_t getPreferredBootModeId() const = 0;
+ virtual int32_t getPreferredBootHwcConfigId() const = 0;
protected:
~Display() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index c553fce..ca86f4c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -72,6 +72,9 @@
virtual void resizeBuffers(const ui::Size&) = 0;
virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
+
+ // Returns true if the render surface supports client composition prediction.
+ virtual bool supportsCompositionStrategyPrediction() const;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index d8644a4..1555102 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -35,6 +35,7 @@
#include <utils/Vector.h>
#include <ui/DisplayIdentification.h>
+#include "DisplayHardware/HWComposer.h"
namespace android {
@@ -54,6 +55,7 @@
namespace impl {
struct OutputCompositionState;
+struct GpuCompositionResult;
} // namespace impl
/**
@@ -262,6 +264,9 @@
// Latches the front-end layer state for each output layer
virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
+ // Enables predicting composition strategy to run client composition earlier
+ virtual void setPredictCompositionStrategy(bool) = 0;
+
protected:
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
@@ -278,13 +283,22 @@
virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
virtual void beginFrame() = 0;
virtual void prepareFrame() = 0;
+
+ using GpuCompositionResult = compositionengine::impl::GpuCompositionResult;
+ // Runs prepare frame in another thread while running client composition using
+ // the previous frame's composition strategy.
+ virtual GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) = 0;
virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
- virtual void finishFrame(const CompositionRefreshArgs&) = 0;
+ virtual void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
- const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
virtual void postFramebuffer() = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
- virtual void chooseCompositionStrategy() = 0;
+ virtual std::optional<android::HWComposer::DeviceRequestedChanges>
+ chooseCompositionStrategy() = 0;
+ virtual void applyCompositionStrategy(
+ const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -295,6 +309,7 @@
std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
virtual void setExpensiveRenderingExpected(bool enabled) = 0;
virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
+ virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index daee83b..9ee779c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -100,6 +100,9 @@
// Debugging - gets the page flip count for the RenderSurface
virtual std::uint32_t getPageFlipCount() const = 0;
+
+ // Returns true if the render surface supports client composition prediction.
+ virtual bool supportsCompositionStrategyPrediction() const = 0;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index ebe112b..3b8b06f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -22,6 +22,7 @@
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
#include <compositionengine/impl/Output.h>
#include <ui/PixelFormat.h>
#include <ui/Size.h>
@@ -51,18 +52,21 @@
void setReleasedLayers(const CompositionRefreshArgs&) override;
void setColorTransform(const CompositionRefreshArgs&) override;
void setColorProfile(const ColorProfile&) override;
- void chooseCompositionStrategy() override;
+
+ using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges;
+ std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override;
+ void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
void setExpensiveRenderingExpected(bool) override;
- void finishFrame(const CompositionRefreshArgs&) override;
+ void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
// compositionengine::Display overrides
DisplayId getId() const override;
bool isSecure() const override;
bool isVirtual() const override;
void disconnect() override;
- int32_t getPreferredBootModeId() const override;
+ int32_t getPreferredBootHwcConfigId() const override;
void createDisplayColorProfile(
const compositionengine::DisplayColorProfileCreationArgs&) override;
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
@@ -73,12 +77,11 @@
using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
- virtual bool anyLayersRequireClientComposition() const;
virtual bool allLayersRequireClientComposition() const;
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
- virtual void applyClientTargetRequests(const ClientTargetProperty&, float whitePointNits);
+ virtual void applyClientTargetRequests(const ClientTargetProperty&, float brightness);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
@@ -88,7 +91,7 @@
DisplayId mId;
bool mIsDisconnected = false;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
- int32_t mPreferredBootDisplayModeId = -1;
+ int32_t mPreferredBootHwcConfigId = -1;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
index 7521324..196af70 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -17,6 +17,7 @@
#pragma once
#include <cstdint>
+#include <optional>
#include <string>
#include <type_traits>
@@ -63,4 +64,13 @@
void dumpVal(std::string& out, const char* name, const mat4&);
void dumpVal(std::string& out, const char* name, const StretchEffect&);
+template <typename T>
+void dumpVal(std::string& out, const char* name, std::optional<T> value) {
+ if (value.has_value()) {
+ return dumpVal(out, name, *value);
+ } else {
+ return dumpVal(out, name, "nullopt");
+ }
+}
+
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
new file mode 100644
index 0000000..ed1ddc1
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::compositionengine::impl {
+
+struct GpuCompositionResult {
+ // True if composition strategy was predicted successfully.
+ bool succeeded = false;
+
+ // Composition ready fence.
+ base::unique_fd fence{};
+
+ // Buffer to be used for gpu composition. If gpu composition was not successful,
+ // then we want to reuse the buffer instead of dequeuing another buffer.
+ std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+
+ bool bufferAvailable() const { return buffer != nullptr; };
+};
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h
new file mode 100644
index 0000000..11c0054
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <future>
+#include <optional>
+#include <thread>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::impl {
+
+// HWC Validate call may take multiple milliseconds to complete and can account for
+// a signification amount of time in the display hotpath. This helper class allows
+// us to run the hwc validate function on a real time thread if we can predict what
+// the composition strategy will be and if composition includes client composition.
+// While the hwc validate runs, client composition is kicked off with the prediction.
+// When the worker returns with a value, the composition continues if the prediction
+// was successful otherwise the client composition is re-executed.
+//
+// Note: This does not alter the sequence between HWC and surfaceflinger.
+class HwcAsyncWorker final {
+public:
+ HwcAsyncWorker();
+ ~HwcAsyncWorker();
+ // Runs the provided function which calls hwc validate and returns the requested
+ // device changes as a future.
+ std::future<std::optional<android::HWComposer::DeviceRequestedChanges>> send(
+ std::function<std::optional<android::HWComposer::DeviceRequestedChanges>()>);
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCv GUARDED_BY(mMutex);
+ bool mDone GUARDED_BY(mMutex) = false;
+ bool mTaskRequested GUARDED_BY(mMutex) = false;
+ std::packaged_task<std::optional<android::HWComposer::DeviceRequestedChanges>()> mTask
+ GUARDED_BY(mMutex);
+ std::thread mThread;
+ void run();
+};
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index a7a8e97..0be5d01 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -17,9 +17,13 @@
#pragma once
#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
#include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
+#include <compositionengine/impl/HwcAsyncWorker.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
@@ -92,25 +96,38 @@
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
+ GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) override;
void devOptRepaintFlash(const CompositionRefreshArgs&) override;
- void finishFrame(const CompositionRefreshArgs&) override;
- std::optional<base::unique_fd> composeSurfaces(
- const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
+ void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+ std::optional<base::unique_fd> composeSurfaces(const Region&,
+ const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&) override;
void postFramebuffer() override;
void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
+ bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
+ void setPredictCompositionStrategy(bool) override;
// Testing
const ReleasedLayers& getReleasedLayersForTest() const;
void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
bool plannerEnabled() const { return mPlanner != nullptr; }
+ virtual bool anyLayersRequireClientComposition() const;
+ virtual void updateProtectedContentState();
+ virtual bool dequeueRenderBuffer(base::unique_fd*,
+ std::shared_ptr<renderengine::ExternalTexture>*);
+ virtual std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>
+ chooseCompositionStrategyAsync();
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
std::optional<size_t> findCurrentOutputLayerForLayer(
const sp<compositionengine::LayerFE>&) const;
- void chooseCompositionStrategy() override;
+ using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges;
+ std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override;
+ void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -131,6 +148,7 @@
private:
void dirtyEntireOutput();
compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
+ void finishPrepareFrame();
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
compositionengine::Output::ColorProfile pickColorProfile(
const compositionengine::CompositionRefreshArgs&) const;
@@ -144,6 +162,7 @@
OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
std::unique_ptr<planner::Planner> mPlanner;
+ std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index cc7c257..92f22b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -37,6 +37,8 @@
#include <ui/Region.h>
#include <ui/Transform.h>
+#include "DisplayHardware/HWComposer.h"
+
namespace android {
namespace compositionengine::impl {
@@ -114,6 +116,8 @@
// Current target dataspace
ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+ std::optional<android::HWComposer::DeviceRequestedChanges> previousDeviceRequestedChanges{};
+
// The earliest time to send the present command to the HAL
std::chrono::steady_clock::time_point earliestPresentTime;
@@ -130,8 +134,8 @@
// SDR white point
float sdrWhitePointNits{-1.f};
- // White point of the client target
- float clientTargetWhitePointNits{-1.f};
+ // Brightness of the client target, normalized to display brightness
+ float clientTargetBrightness{1.f};
// Display brightness that will take effect this frame.
// This is slightly distinct from nits, in that nits cannot be passed to hw composer.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index a8a5380..e4cb113 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -63,6 +63,7 @@
void queueBuffer(base::unique_fd readyFence) override;
void onPresentDisplayCompleted() override;
void flip() override;
+ bool supportsCompositionStrategyPrediction() const override;
// Debugging
void dump(std::string& result) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 84afb59..72e6f3b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -34,13 +34,14 @@
MOCK_CONST_METHOD0(getId, DisplayId());
MOCK_CONST_METHOD0(isSecure, bool());
MOCK_CONST_METHOD0(isVirtual, bool());
- MOCK_CONST_METHOD0(getPreferredBootModeId, int32_t());
+ MOCK_CONST_METHOD0(getPreferredBootHwcConfigId, int32_t());
MOCK_METHOD0(disconnect, void());
MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+ MOCK_METHOD1(setPredictCompositionStrategy, void(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 b68b95d..27303a8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -22,6 +22,7 @@
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <gmock/gmock.h>
@@ -99,16 +100,22 @@
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
- MOCK_METHOD0(chooseCompositionStrategy, void());
+ MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+ MOCK_METHOD0(chooseCompositionStrategy,
+ std::optional<android::HWComposer::DeviceRequestedChanges>());
+ MOCK_METHOD1(applyCompositionStrategy,
+ void(const std::optional<android::HWComposer::DeviceRequestedChanges>&));
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD2(finishFrame,
+ void(const compositionengine::CompositionRefreshArgs&, GpuCompositionResult&&));
- MOCK_METHOD2(composeSurfaces,
+ MOCK_METHOD4(composeSurfaces,
std::optional<base::unique_fd>(
const Region&,
- const compositionengine::CompositionRefreshArgs& refreshArgs));
+ const compositionengine::CompositionRefreshArgs& refreshArgs,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
@@ -121,6 +128,8 @@
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
MOCK_METHOD1(cacheClientCompositionRequests, void(uint32_t));
+ MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
+ MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index fe858c2..e12aebb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -45,6 +45,7 @@
MOCK_METHOD0(flip, void());
MOCK_CONST_METHOD1(dump, void(std::string& result));
MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
+ MOCK_CONST_METHOD0(supportsCompositionStrategyPrediction, bool());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index a3188f3..09648c3 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -66,7 +66,7 @@
std::optional<hal::HWConfigId> preferredBootModeId =
getCompositionEngine().getHwComposer().getPreferredBootDisplayMode(*physicalId);
if (preferredBootModeId.has_value()) {
- mPreferredBootDisplayModeId = static_cast<int32_t>(preferredBootModeId.value());
+ mPreferredBootHwcConfigId = static_cast<int32_t>(preferredBootModeId.value());
}
}
@@ -90,8 +90,8 @@
return mId;
}
-int32_t Display::getPreferredBootModeId() const {
- return mPreferredBootDisplayModeId;
+int32_t Display::getPreferredBootHwcConfigId() const {
+ return mPreferredBootHwcConfigId;
}
void Display::disconnect() {
@@ -221,12 +221,12 @@
setReleasedLayers(std::move(releasedLayers));
}
-void Display::chooseCompositionStrategy() {
+std::optional<android::HWComposer::DeviceRequestedChanges> Display::chooseCompositionStrategy() {
ATRACE_CALL();
ALOGV(__FUNCTION__);
if (mIsDisconnected) {
- return;
+ return {};
}
// Default to the base settings -- client composition only.
@@ -235,7 +235,7 @@
// If we don't have a HWC display, then we are done.
const auto halDisplayId = HalDisplayId::tryCast(mId);
if (!halDisplayId) {
- return;
+ return {};
}
// Get any composition changes requested by the HWC device, and apply them.
@@ -260,14 +260,18 @@
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
- return;
+ return {};
}
+
+ return changes;
+}
+
+void Display::applyCompositionStrategy(const std::optional<DeviceRequestedChanges>& changes) {
if (changes) {
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
- applyClientTargetRequests(changes->clientTargetProperty,
- changes->clientTargetWhitePointNits);
+ applyClientTargetRequests(changes->clientTargetProperty, changes->clientTargetBrightness);
}
// Determine what type of composition we are doing from the final state
@@ -288,12 +292,6 @@
return hwc.hasCapability(Capability::SKIP_CLIENT_COLOR_TRANSFORM);
}
-bool Display::anyLayersRequireClientComposition() const {
- const auto layers = getOutputLayersOrderedByZ();
- return std::any_of(layers.begin(), layers.end(),
- [](const auto& layer) { return layer->requiresClientComposition(); });
-}
-
bool Display::allLayersRequireClientComposition() const {
const auto layers = getOutputLayersOrderedByZ();
return std::all_of(layers.begin(), layers.end(),
@@ -343,13 +341,13 @@
}
void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty,
- float whitePointNits) {
+ float brightness) {
if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
return;
}
editState().dataspace = clientTargetProperty.dataspace;
- editState().clientTargetWhitePointNits = whitePointNits;
+ editState().clientTargetBrightness = brightness;
getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
}
@@ -391,7 +389,8 @@
}
}
-void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
+ GpuCompositionResult&& result) {
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
@@ -401,7 +400,7 @@
return;
}
- impl::Output::finishFrame(refreshArgs);
+ impl::Output::finishFrame(refreshArgs, std::move(result));
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
index db6d4f2..28900af 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
@@ -20,4 +20,8 @@
DisplaySurface::~DisplaySurface() = default;
+bool DisplaySurface::supportsCompositionStrategyPrediction() const {
+ return true;
+}
+
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
new file mode 100644
index 0000000..497424a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/HwcAsyncWorker.h>
+#include <processgroup/sched_policy.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <system/thread_defs.h>
+
+#include <android-base/thread_annotations.h>
+#include <cutils/sched_policy.h>
+
+namespace android::compositionengine::impl {
+
+HwcAsyncWorker::HwcAsyncWorker() {
+ mThread = std::thread(&HwcAsyncWorker::run, this);
+ pthread_setname_np(mThread.native_handle(), "HwcAsyncWorker");
+}
+
+HwcAsyncWorker::~HwcAsyncWorker() {
+ {
+ std::scoped_lock lock(mMutex);
+ mDone = true;
+ mCv.notify_all();
+ }
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+std::future<std::optional<android::HWComposer::DeviceRequestedChanges>> HwcAsyncWorker::send(
+ std::function<std::optional<android::HWComposer::DeviceRequestedChanges>()> task) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ android::base::ScopedLockAssertion assumeLock(mMutex);
+ mTask = std::packaged_task<std::optional<android::HWComposer::DeviceRequestedChanges>()>(
+ [task = std::move(task)]() { return task(); });
+ mTaskRequested = true;
+ mCv.notify_one();
+ return mTask.get_future();
+}
+
+void HwcAsyncWorker::run() {
+ set_sched_policy(0, SP_FOREGROUND);
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ sched_setscheduler(gettid(), SCHED_FIFO, ¶m);
+
+ std::unique_lock<std::mutex> lock(mMutex);
+ android::base::ScopedLockAssertion assumeLock(mMutex);
+ while (!mDone) {
+ mCv.wait(lock);
+ if (mTaskRequested && mTask.valid()) {
+ mTask();
+ mTaskRequested = false;
+ }
+ }
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e036252..8d560d7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -22,6 +22,7 @@
#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/HwcAsyncWorker.h>
#include <compositionengine/impl/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
@@ -434,9 +435,17 @@
writeCompositionState(refreshArgs);
setColorTransform(refreshArgs);
beginFrame();
- prepareFrame();
+
+ GpuCompositionResult result;
+ const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs);
+ if (predictCompositionStrategy) {
+ result = prepareFrameAsync(refreshArgs);
+ } else {
+ prepareFrame();
+ }
+
devOptRepaintFlash(refreshArgs);
- finishFrame(refreshArgs);
+ finishFrame(refreshArgs, std::move(result));
postFramebuffer();
renderCachedSets(refreshArgs);
}
@@ -951,19 +960,62 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
- const auto& outputState = getState();
+ auto& outputState = editState();
if (!outputState.isEnabled) {
return;
}
- chooseCompositionStrategy();
+ auto changes = chooseCompositionStrategy();
+ outputState.previousDeviceRequestedChanges = changes;
+ if (changes) {
+ applyCompositionStrategy(changes);
+ }
+ finishPrepareFrame();
+}
- if (mPlanner) {
- mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>
+Output::chooseCompositionStrategyAsync() {
+ return mHwComposerAsyncWorker->send([&]() { return chooseCompositionStrategy(); });
+}
+
+GpuCompositionResult Output::prepareFrameAsync(const CompositionRefreshArgs& refreshArgs) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+ auto& state = editState();
+ const auto& previousChanges = state.previousDeviceRequestedChanges;
+ auto hwcResult = chooseCompositionStrategyAsync();
+ applyCompositionStrategy(previousChanges);
+ finishPrepareFrame();
+
+ base::unique_fd bufferFence;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
+ updateProtectedContentState();
+ const bool dequeueSucceeded = dequeueRenderBuffer(&bufferFence, &buffer);
+ GpuCompositionResult compositionResult;
+ if (dequeueSucceeded) {
+ std::optional<base::unique_fd> optFd =
+ composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ if (optFd) {
+ compositionResult.fence = std::move(*optFd);
+ }
}
- mRenderSurface->prepareFrame(outputState.usesClientComposition,
- outputState.usesDeviceComposition);
+ auto changes = hwcResult.valid() ? hwcResult.get() : std::nullopt;
+ const bool predictionSucceeded = dequeueSucceeded && changes == previousChanges;
+ compositionResult.succeeded = predictionSucceeded;
+ if (!predictionSucceeded) {
+ ATRACE_NAME("CompositionStrategyPredictionMiss");
+ if (changes) {
+ applyCompositionStrategy(changes);
+ }
+ finishPrepareFrame();
+ // Track the dequeued buffer to reuse so we don't need to dequeue another one.
+ compositionResult.buffer = buffer;
+ } else {
+ ATRACE_NAME("CompositionStrategyPredictionHit");
+ }
+ state.previousDeviceRequestedChanges = std::move(changes);
+ return compositionResult;
}
void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -973,7 +1025,11 @@
if (getState().isEnabled) {
if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) {
- static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
+ base::unique_fd bufferFence;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
+ updateProtectedContentState();
+ dequeueRenderBuffer(&bufferFence, &buffer);
+ static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs, buffer, bufferFence));
mRenderSurface->queueBuffer(base::unique_fd());
}
}
@@ -985,7 +1041,7 @@
prepareFrame();
}
-void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -993,9 +1049,25 @@
return;
}
- // Repaint the framebuffer (if needed), getting the optional fence for when
- // the composition completes.
- auto optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs);
+ std::optional<base::unique_fd> optReadyFence;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
+ base::unique_fd bufferFence;
+ if (result.succeeded) {
+ optReadyFence = std::move(result.fence);
+ } else {
+ if (result.bufferAvailable()) {
+ buffer = std::move(result.buffer);
+ bufferFence = std::move(result.fence);
+ } else {
+ updateProtectedContentState();
+ if (!dequeueRenderBuffer(&bufferFence, &buffer)) {
+ return;
+ }
+ }
+ // Repaint the framebuffer (if needed), getting the optional fence for when
+ // the composition completes.
+ optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ }
if (!optReadyFence) {
return;
}
@@ -1004,16 +1076,8 @@
mRenderSurface->queueBuffer(std::move(*optReadyFence));
}
-std::optional<base::unique_fd> Output::composeSurfaces(
- const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs) {
- ATRACE_CALL();
- ALOGV(__FUNCTION__);
-
+void Output::updateProtectedContentState() {
const auto& outputState = getState();
- OutputCompositionState& outputCompositionState = editState();
- const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
- outputState.usesClientComposition};
-
auto& renderEngine = getCompositionEngine().getRenderEngine();
const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
@@ -1035,29 +1099,48 @@
} else if (!outputState.isSecure && renderEngine.isProtected()) {
renderEngine.useProtectedContext(false);
}
+}
- base::unique_fd fd;
-
- std::shared_ptr<renderengine::ExternalTexture> tex;
+bool Output::dequeueRenderBuffer(base::unique_fd* bufferFence,
+ std::shared_ptr<renderengine::ExternalTexture>* tex) {
+ const auto& outputState = getState();
// If we aren't doing client composition on this output, but do have a
// flipClientTarget request for this frame on this output, we still need to
// dequeue a buffer.
- if (hasClientComposition || outputState.flipClientTarget) {
- tex = mRenderSurface->dequeueBuffer(&fd);
- if (tex == nullptr) {
+ if (outputState.usesClientComposition || outputState.flipClientTarget) {
+ *tex = mRenderSurface->dequeueBuffer(bufferFence);
+ if (*tex == nullptr) {
ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
"client composition for this frame",
mName.c_str());
- return {};
+ return false;
}
}
+ return true;
+}
+std::optional<base::unique_fd> Output::composeSurfaces(
+ const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs,
+ std::shared_ptr<renderengine::ExternalTexture> tex, base::unique_fd& fd) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ const auto& outputState = getState();
+ const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
+ outputState.usesClientComposition};
if (!hasClientComposition) {
setExpensiveRenderingExpected(false);
return base::unique_fd();
}
+ if (tex == nullptr) {
+ ALOGW("Buffer not valid for display [%s], bailing out of "
+ "client composition for this frame",
+ mName.c_str());
+ return {};
+ }
+
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
@@ -1076,7 +1159,8 @@
: mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
clientCompositionDisplay.maxLuminance =
mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
- clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits;
+ clientCompositionDisplay.targetLuminanceNits =
+ outputState.clientTargetBrightness * outputState.displayBrightnessNits;
// Compute the global color transform matrix.
clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
@@ -1084,6 +1168,8 @@
outputState.usesDeviceComposition || getSkipColorTransform();
// Generate the client composition requests for the layers on this output.
+ auto& renderEngine = getCompositionEngine().getRenderEngine();
+ const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
std::vector<LayerFE*> clientCompositionLayersFE;
std::vector<LayerFE::LayerSettings> clientCompositionLayers =
generateClientCompositionRequests(supportsProtectedContent,
@@ -1091,16 +1177,19 @@
clientCompositionLayersFE);
appendRegionFlashRequests(debugRegion, clientCompositionLayers);
+ OutputCompositionState& outputCompositionState = editState();
// Check if the client composition requests were rendered into the provided graphic buffer. If
// so, we can reuse the buffer and avoid client composition.
if (mClientCompositionRequestCache) {
if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(),
clientCompositionDisplay,
clientCompositionLayers)) {
+ ATRACE_NAME("ClientCompositionCacheHit");
outputCompositionState.reusedClientComposition = true;
setExpensiveRenderingExpected(false);
return base::unique_fd();
}
+ ATRACE_NAME("ClientCompositionCacheMiss");
mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
clientCompositionLayers);
}
@@ -1348,12 +1437,13 @@
outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
}
-void Output::chooseCompositionStrategy() {
+std::optional<android::HWComposer::DeviceRequestedChanges> Output::chooseCompositionStrategy() {
// The base output implementation can only do client composition
auto& outputState = editState();
outputState.usesClientComposition = true;
outputState.usesDeviceComposition = false;
outputState.reusedClientComposition = false;
+ return {};
}
bool Output::getSkipColorTransform() const {
@@ -1368,5 +1458,63 @@
return result;
}
+void Output::setPredictCompositionStrategy(bool predict) {
+ if (predict) {
+ mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>();
+ } else {
+ mHwComposerAsyncWorker.reset(nullptr);
+ }
+}
+
+bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refreshArgs) {
+ if (!getState().isEnabled || !mHwComposerAsyncWorker) {
+ ALOGV("canPredictCompositionStrategy disabled");
+ return false;
+ }
+
+ if (!getState().previousDeviceRequestedChanges) {
+ ALOGV("canPredictCompositionStrategy previous changes not available");
+ return false;
+ }
+
+ if (!mRenderSurface->supportsCompositionStrategyPrediction()) {
+ ALOGV("canPredictCompositionStrategy surface does not support");
+ return false;
+ }
+
+ if (refreshArgs.devOptFlashDirtyRegionsDelay) {
+ ALOGV("canPredictCompositionStrategy devOptFlashDirtyRegionsDelay");
+ return false;
+ }
+
+ // If no layer uses clientComposition, then don't predict composition strategy
+ // because we have less work to do in parallel.
+ if (!anyLayersRequireClientComposition()) {
+ ALOGV("canPredictCompositionStrategy no layer uses clientComposition");
+ return false;
+ }
+
+ if (!refreshArgs.updatingOutputGeometryThisFrame) {
+ return true;
+ }
+
+ ALOGV("canPredictCompositionStrategy updatingOutputGeometryThisFrame");
+ return false;
+}
+
+bool Output::anyLayersRequireClientComposition() const {
+ const auto layers = getOutputLayersOrderedByZ();
+ return std::any_of(layers.begin(), layers.end(),
+ [](const auto& layer) { return layer->requiresClientComposition(); });
+}
+
+void Output::finishPrepareFrame() {
+ const auto& state = getState();
+ if (mPlanner) {
+ mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+ }
+ mRenderSurface->prepareFrame(state.usesClientComposition, state.usesDeviceComposition);
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index acc9216..482250a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -52,6 +52,10 @@
dumpVal(out, "dataspace", toString(dataspace), dataspace);
dumpVal(out, "colorTransformMatrix", colorTransformMatrix);
dumpVal(out, "target dataspace", toString(targetDataspace), targetDataspace);
+ dumpVal(out, "displayBrightnessNits", displayBrightnessNits);
+ dumpVal(out, "sdrWhitePointNits", sdrWhitePointNits);
+ dumpVal(out, "clientTargetBrightness", clientTargetBrightness);
+ dumpVal(out, "displayBrightness", displayBrightness);
out.append("\n");
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index cfa740e..6749427 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -67,6 +67,8 @@
dumpVal(out, "sourceCrop", sourceCrop);
dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
dumpVal(out, "dataspace", toString(dataspace), dataspace);
+ dumpVal(out, "whitePointNits", whitePointNits);
+ dumpVal(out, "dimmingRatio", dimmingRatio);
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/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 12c2c8e..5a3af7b 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -289,5 +289,9 @@
return mTexture;
}
+bool RenderSurface::supportsCompositionStrategyPrediction() const {
+ return mDisplaySurface->supportsCompositionStrategyPrediction();
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 44d34a3..36b04d9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -30,7 +30,9 @@
#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
#include <gtest/gtest.h>
+#include <renderengine/mock/FakeExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+
#include <ui/Rect.h>
#include <ui/StaticDisplayInfo.h>
@@ -198,6 +200,22 @@
std::shared_ptr<Display> mDisplay =
createPartialMockDisplay<Display>(mCompositionEngine,
getDisplayCreationArgsForPhysicalDisplay());
+
+ android::HWComposer::DeviceRequestedChanges mDeviceRequestedChanges{
+ {{nullptr, Composition::CLIENT}},
+ hal::DisplayRequest::FLIP_CLIENT_TARGET,
+ {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
+ {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+ -1.f,
+ };
+
+ void chooseCompositionStrategy(Display* display) {
+ std::optional<android::HWComposer::DeviceRequestedChanges> changes =
+ display->chooseCompositionStrategy();
+ if (changes) {
+ display->applyCompositionStrategy(changes);
+ }
+ }
};
struct FullDisplayImplTestCommon : public DisplayTestCommon {
@@ -214,6 +232,11 @@
std::unique_ptr<compositionengine::OutputLayer>(mLayer2.outputLayer));
mDisplay->injectOutputLayerForTest(
std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
+ mResultWithBuffer.buffer = std::make_shared<
+ renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ 1ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0ULL /*usage*/);
}
Layer mLayer1;
@@ -222,6 +245,8 @@
StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
std::shared_ptr<Display> mDisplay =
createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
+ impl::GpuCompositionResult mResultWithBuffer;
+ impl::GpuCompositionResult mResultWithoutBuffer;
};
/*
@@ -554,7 +579,7 @@
createPartialMockDisplay<Display>(mCompositionEngine, args);
EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
- gpuDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(gpuDisplay.get());
auto& state = gpuDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
@@ -567,11 +592,12 @@
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
.WillOnce(Return(INVALID_OPERATION));
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
EXPECT_FALSE(state.usesDeviceComposition);
+ EXPECT_FALSE(state.previousDeviceRequestedChanges.has_value());
}
TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) {
@@ -588,10 +614,16 @@
EXPECT_CALL(mHwComposer,
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
- .WillOnce(Return(NO_ERROR));
+ .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
+ Return(NO_ERROR)));
+ EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+ .Times(1);
+ EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+ .Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
@@ -618,11 +650,17 @@
EXPECT_CALL(mHwComposer,
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
- .WillOnce(Return(NO_ERROR));
+ .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
+ Return(NO_ERROR)));
+ EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+ .Times(1);
+ EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+ .Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
mDisplay->setNextBrightness(kDisplayBrightness);
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
@@ -631,14 +669,6 @@
}
TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
- android::HWComposer::DeviceRequestedChanges changes{
- {{nullptr, Composition::CLIENT}},
- hal::DisplayRequest::FLIP_CLIENT_TARGET,
- {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
- {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
- -1.f,
- };
-
// Since two calls are made to anyLayersRequireClientComposition with different return
// values, use a Sequence to control the matching so the values are returned in a known
// order.
@@ -652,13 +682,15 @@
EXPECT_CALL(mHwComposer,
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<5>(changes), Return(NO_ERROR)));
- EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
- EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
- EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
+ .WillOnce(DoAll(SetArgPointee<5>(mDeviceRequestedChanges), Return(NO_ERROR)));
+ EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+ .Times(1);
+ EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+ .Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
@@ -839,7 +871,7 @@
auto& state = mDisplay->getState();
EXPECT_EQ(clientTargetProperty.dataspace, state.dataspace);
- EXPECT_EQ(kWhitePointNits, state.clientTargetWhitePointNits);
+ EXPECT_EQ(kWhitePointNits, state.clientTargetBrightness);
}
/*
@@ -922,7 +954,7 @@
mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- mDisplay->finishFrame({});
+ mDisplay->finishFrame({}, std::move(mResultWithBuffer));
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -940,7 +972,7 @@
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- gpuDisplay->finishFrame({});
+ gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
}
TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
@@ -957,7 +989,7 @@
gpuDisplay->editState().usesClientComposition = false;
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
- gpuDisplay->finishFrame({});
+ gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cab4b8d..4e875c8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -28,6 +28,7 @@
#include <gtest/gtest.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/impl/ExternalTexture.h>
+#include <renderengine/mock/FakeExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -989,7 +990,7 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(chooseCompositionStrategy, void());
+ MOCK_METHOD0(chooseCompositionStrategy, std::optional<DeviceRequestedChanges>());
};
OutputPrepareFrameTest() {
@@ -1037,6 +1038,133 @@
EXPECT_FALSE(mOutput->getState().usesDeviceComposition);
}
+struct OutputPrepareFrameAsyncTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD0(chooseCompositionStrategy, std::optional<DeviceRequestedChanges>());
+ MOCK_METHOD0(updateProtectedContentState, void());
+ MOCK_METHOD2(dequeueRenderBuffer,
+ bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
+ MOCK_METHOD0(chooseCompositionStrategyAsync,
+ std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>());
+ MOCK_METHOD4(composeSurfaces,
+ std::optional<base::unique_fd>(
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ };
+
+ OutputPrepareFrameAsyncTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ StrictMock<OutputPartialMock> mOutput;
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputPrepareFrameAsyncTest, delegatesToChooseCompositionStrategyAndRenderSurface) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value(mOutput.editState().previousDeviceRequestedChanges);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+ EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_TRUE(result.succeeded);
+ EXPECT_FALSE(result.bufferAvailable());
+}
+
+TEST_F(OutputPrepareFrameAsyncTest, skipCompositionOnDequeueFailure) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value(mOutput.editState().previousDeviceRequestedChanges);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_FALSE(result.succeeded);
+ EXPECT_FALSE(result.bufferAvailable());
+}
+
+// Tests that in the event of hwc error when choosing composition strategy, we would fall back
+// client composition
+TEST_F(OutputPrepareFrameAsyncTest, chooseCompositionStrategyFailureCallsPrepareFrame) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value({});
+ std::shared_ptr<renderengine::ExternalTexture> tex =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(tex), Return(true)));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+ EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_FALSE(result.succeeded);
+ EXPECT_TRUE(result.bufferAvailable());
+}
+
+TEST_F(OutputPrepareFrameAsyncTest, predictionMiss) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ auto newDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ newDeviceRequestedChanges->clientTargetBrightness = 5.f;
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value(newDeviceRequestedChanges);
+ std::shared_ptr<renderengine::ExternalTexture> tex =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(tex), Return(true)));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+ EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_FALSE(result.succeeded);
+ EXPECT_TRUE(result.bufferAvailable());
+}
+
/*
* Output::prepare()
*/
@@ -1795,10 +1923,14 @@
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
+ MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD2(finishFrame,
+ void(const compositionengine::CompositionRefreshArgs&,
+ GpuCompositionResult&&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
};
StrictMock<OutputPartialMock> mOutput;
@@ -1814,9 +1946,30 @@
EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
+ EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false));
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
- EXPECT_CALL(mOutput, finishFrame(Ref(args)));
+ EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
+
+ mOutput.present(args);
+}
+
+TEST_F(OutputPresentTest, predictingCompositionStrategyInvokesPrepareFrameAsync) {
+ CompositionRefreshArgs args;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
+ EXPECT_CALL(mOutput, updateCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, planComposition());
+ EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
+ EXPECT_CALL(mOutput, beginFrame());
+ EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true));
+ EXPECT_CALL(mOutput, prepareFrameAsync(Ref(args)));
+ EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
+ EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
@@ -2714,11 +2867,15 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD(Region, getDirtyRegion, (), (const));
- MOCK_METHOD2(composeSurfaces,
+ MOCK_METHOD4(composeSurfaces,
std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&));
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(prepareFrame, void());
+ MOCK_METHOD0(updateProtectedContentState, void());
+ MOCK_METHOD2(dequeueRenderBuffer,
+ bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
};
OutputDevOptRepaintFlashTest() {
@@ -2775,7 +2932,9 @@
InSequence seq;
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, prepareFrame());
@@ -2791,10 +2950,14 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD2(composeSurfaces,
+ MOCK_METHOD4(composeSurfaces,
std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&));
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(updateProtectedContentState, void());
+ MOCK_METHOD2(dequeueRenderBuffer,
+ bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
};
OutputFinishFrameTest() {
@@ -2812,27 +2975,63 @@
TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
mOutput.mState.isEnabled = false;
- mOutput.finishFrame(mRefreshArgs);
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
}
TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
mOutput.mState.isEnabled = true;
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _));
- InSequence seq;
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _));
-
- mOutput.finishFrame(mRefreshArgs);
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
}
TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
mOutput.mState.isEnabled = true;
InSequence seq;
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _))
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _))
.WillOnce(Return(ByMove(base::unique_fd())));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
- mOutput.finishFrame(mRefreshArgs);
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
+}
+
+TEST_F(OutputFinishFrameTest, predictionSucceeded) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+
+ impl::GpuCompositionResult result;
+ result.succeeded = true;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
+}
+
+TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+
+ impl::GpuCompositionResult result;
+ result.succeeded = false;
+ result.buffer =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+
+ EXPECT_CALL(mOutput,
+ composeSurfaces(RegionEq(Region::INVALID_REGION), _, result.buffer,
+ Eq(ByRef(result.fence))))
+ .WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
}
/*
@@ -3067,7 +3266,7 @@
mOutput.mState.usesDeviceComposition = false;
mOutput.mState.reusedClientComposition = false;
mOutput.mState.flipClientTarget = false;
- mOutput.mState.clientTargetWhitePointNits = kClientTargetLuminanceNits;
+ mOutput.mState.clientTargetBrightness = kClientTargetBrightness;
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
@@ -3079,8 +3278,15 @@
struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
auto execute() {
- getInstance()->mReadyFence =
- getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fence;
+ std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+ const bool success =
+ getInstance()->mOutput.dequeueRenderBuffer(&fence, &externalTexture);
+ if (success) {
+ getInstance()->mReadyFence =
+ getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs,
+ externalTexture, fence);
+ }
return nextState<FenceCheckState>();
}
};
@@ -3103,8 +3309,9 @@
static constexpr float kDefaultAvgLuminance = 0.7f;
static constexpr float kDefaultMinLuminance = 0.1f;
static constexpr float kUnknownLuminance = -1.f;
- static constexpr float kDisplayLuminance = 80.f;
+ static constexpr float kDisplayLuminance = 400.f;
static constexpr float kClientTargetLuminanceNits = 200.f;
+ static constexpr float kClientTargetBrightness = 0.5f;
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
@@ -3640,7 +3847,11 @@
EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
EXPECT_CALL(mRenderEngine, useProtectedContext(false));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
@@ -3648,7 +3859,11 @@
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
@@ -3660,7 +3875,11 @@
EXPECT_CALL(mRenderEngine, useProtectedContext(false));
EXPECT_CALL(*mRenderSurface, setProtected(false));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
@@ -3682,7 +3901,11 @@
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
@@ -3692,7 +3915,11 @@
EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
@@ -3703,7 +3930,11 @@
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
EXPECT_CALL(mRenderEngine, useProtectedContext(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
@@ -3714,7 +3945,11 @@
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
EXPECT_CALL(*mRenderSurface, setProtected(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
@@ -3725,7 +3960,11 @@
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
EXPECT_CALL(mRenderEngine, useProtectedContext(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
@@ -3754,7 +3993,11 @@
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
@@ -3789,7 +4032,12 @@
mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
@@ -3799,7 +4047,12 @@
mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
}
/*
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index eef0052..610d86f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -91,6 +91,7 @@
static_cast<uint32_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers));
}
+ mCompositionDisplay->setPredictCompositionStrategy(mFlinger->mPredictCompositionStrategy);
mCompositionDisplay->createDisplayColorProfile(
compositionengine::DisplayColorProfileCreationArgsBuilder()
.setHasWideColorGamut(args.hasWideColorGamut)
@@ -235,6 +236,15 @@
return nullptr;
}
+DisplayModePtr DisplayDevice::getModefromHwcId(uint32_t hwcId) const {
+ const auto it = std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
+ [&](DisplayModePtr mode) { return mode->getHwcId() == hwcId; });
+ if (it != mSupportedModes.end()) {
+ return *it;
+ }
+ return nullptr;
+}
+
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
const auto physicalId = getPhysicalId();
if (!mHwComposer.isConnected(physicalId)) {
@@ -460,7 +470,13 @@
}
ui::DisplayModeId DisplayDevice::getPreferredBootModeId() const {
- return mCompositionDisplay->getPreferredBootModeId();
+ const auto preferredBootHwcModeId = mCompositionDisplay->getPreferredBootHwcConfigId();
+ const auto mode = getModefromHwcId(preferredBootHwcModeId);
+ if (mode == nullptr) {
+ ALOGE("%s: invalid display mode (%d)", __FUNCTION__, preferredBootHwcModeId);
+ return BAD_VALUE;
+ }
+ return mode->getId().value();
}
void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index bf6b31a..690f240 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -232,6 +232,9 @@
// set-top boxes after a hotplug reconnect.
DisplayModePtr getMode(DisplayModeId) const;
+ // Returns nullptr if the given mode ID is not supported.
+ DisplayModePtr getModefromHwcId(uint32_t) const;
+
// Returns the refresh rate configs for this display.
scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index b68231f..80e2d99 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1069,11 +1069,11 @@
Error AidlComposer::getClientTargetProperty(
Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
- float* whitePointNits) {
+ float* outBrightness) {
const auto property = mReader.takeClientTargetProperty(translate<int64_t>(display));
*outClientTargetProperty =
translate<IComposerClient::ClientTargetProperty>(property.clientTargetProperty);
- *whitePointNits = property.whitePointNits;
+ *outBrightness = property.brightness;
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 724c6c9..80ca8da 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -209,7 +209,7 @@
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
Error getClientTargetProperty(Display display,
IComposerClient::ClientTargetProperty* outClientTargetProperty,
- float* outClientTargetWhitePointNits) override;
+ float* outBrightness) override;
// AIDL Composer HAL
Error setLayerBrightness(Display display, Layer layer, float brightness) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 06aaa11..23886d4 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -261,7 +261,7 @@
virtual Error getClientTargetProperty(
Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
- float* outWhitePointNits) = 0;
+ float* outBrightness) = 0;
// AIDL Composer
virtual Error setLayerBrightness(Display display, Layer layer, float brightness) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 6a3162e..ed6e4b0 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -479,13 +479,12 @@
RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
- float clientTargetWhitePointNits;
- error = hwcDisplay->getClientTargetProperty(&clientTargetProperty, &clientTargetWhitePointNits);
+ float brightness = 1.f;
+ error = hwcDisplay->getClientTargetProperty(&clientTargetProperty, &brightness);
outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
std::move(layerRequests),
- std::move(clientTargetProperty),
- clientTargetWhitePointNits});
+ std::move(clientTargetProperty), brightness});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 916c4b7..a8d439b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -86,7 +86,7 @@
DisplayRequests displayRequests;
LayerRequests layerRequests;
ClientTargetProperty clientTargetProperty;
- float clientTargetWhitePointNits;
+ float clientTargetBrightness;
};
struct HWCDisplayMode {
@@ -269,6 +269,14 @@
support) = 0;
};
+static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
+ const android::HWComposer::DeviceRequestedChanges& rhs) {
+ return lhs.changedTypes == rhs.changedTypes && lhs.displayRequests == rhs.displayRequests &&
+ lhs.layerRequests == rhs.layerRequests &&
+ lhs.clientTargetProperty == rhs.clientTargetProperty &&
+ lhs.clientTargetBrightness == rhs.clientTargetBrightness;
+}
+
namespace impl {
class HWComposer final : public android::HWComposer {
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 95d7b58..f735bba 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1297,9 +1297,9 @@
Error HidlComposer::getClientTargetProperty(
Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
- float* outWhitePointNits) {
+ float* outBrightness) {
mReader.takeClientTargetProperty(display, outClientTargetProperty);
- *outWhitePointNits = -1.f;
+ *outBrightness = 1.f;
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 71ae8b4..c2b60cb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -318,7 +318,7 @@
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
Error getClientTargetProperty(Display display,
IComposerClient::ClientTargetProperty* outClientTargetProperty,
- float* outWhitePointNits) override;
+ float* outBrightness) override;
// AIDL Composer HAL
Error setLayerBrightness(Display display, Layer layer, float brightness) override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 6f02843..3dab389 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -16,6 +16,8 @@
//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#undef LOG_TAG
#define LOG_TAG "PowerAdvisor"
@@ -27,6 +29,7 @@
#include <android-base/properties.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
+#include <utils/Trace.h>
#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/IPower.h>
@@ -70,6 +73,14 @@
return timeout;
}
+void traceExpensiveRendering(bool enabled) {
+ if (enabled) {
+ ATRACE_ASYNC_BEGIN("ExpensiveRendering", 0);
+ } else {
+ ATRACE_ASYNC_END("ExpensiveRendering", 0);
+ }
+}
+
} // namespace
PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
@@ -247,6 +258,9 @@
bool setExpensiveRendering(bool enabled) override {
ALOGV("HIDL setExpensiveRendering %s", enabled ? "T" : "F");
auto ret = mPowerHal->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING, enabled);
+ if (ret.isOk()) {
+ traceExpensiveRendering(enabled);
+ }
return ret.isOk();
}
@@ -323,6 +337,9 @@
}
auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
+ if (ret.isOk()) {
+ traceExpensiveRendering(enabled);
+ }
return ret.isOk();
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 307da41..e21095a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -89,6 +89,9 @@
virtual void dumpAsString(String8& result) const;
virtual void resizeBuffers(const ui::Size&) override;
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
+ // Virtual display surface needs to prepare the frame based on composition type. Skip
+ // any client composition prediction.
+ virtual bool supportsCompositionStrategyPrediction() const override { return false; };
private:
enum Source : size_t {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8081096..533acfd 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2142,6 +2142,9 @@
(*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
}
}
+
+ LayerProtoHelper::writeToProto(state.destinationFrame,
+ [&]() { return layerInfo->mutable_destination_frame(); });
}
bool Layer::isRemovedFromCurrentState() const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index eeeaac1..15e30b3 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -202,24 +202,24 @@
}
if (layer.vote == LayerVoteType::ExplicitExact) {
- const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate);
+ const int divisor = getFrameRateDivisor(refreshRate.getFps(), layer.desiredRefreshRate);
if (mSupportsFrameRateOverrideByContent) {
// Since we support frame rate override, allow refresh rates which are
// multiples of the layer's request, as those apps would be throttled
// down to run at the desired refresh rate.
- return divider > 0;
+ return divisor > 0;
}
- return divider == 1;
+ return divisor == 1;
}
- // If the layer frame rate is a divider of the refresh rate it should score
+ // If the layer frame rate is a divisor of the refresh rate it should score
// the highest score.
- if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+ if (getFrameRateDivisor(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
return 1.0f * seamlessness;
}
- // The layer frame rate is not a divider of the refresh rate,
+ // The layer frame rate is not a divisor of the refresh rate,
// there is a small penalty attached to the score to favor the frame rates
// the exactly matches the display refresh rate or a multiple.
constexpr float kNonExactMatchingPenalty = 0.95f;
@@ -543,11 +543,11 @@
}
}
- // We just care about the refresh rates which are a divider of the
+ // We just care about the refresh rates which are a divisor of the
// display refresh rate
auto iter =
std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) {
- return getFrameRateDivider(displayFrameRate, score.refreshRate->getFps()) == 0;
+ return getFrameRateDivisor(displayFrameRate, score.refreshRate->getFps()) == 0;
});
scores.erase(iter, scores.end());
@@ -723,7 +723,7 @@
if (mConfig.enableFrameRateOverride) {
for (const auto& mode1 : sortedModes) {
for (const auto& mode2 : sortedModes) {
- if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
+ if (getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
mSupportsFrameRateOverrideByContent = true;
break;
}
@@ -915,7 +915,7 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
-int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
+int RefreshRateConfigs::getFrameRateDivisor(Fps displayFrameRate, Fps layerFrameRate) {
// This calculation needs to be in sync with the java code
// in DisplayManagerService.getDisplayInfoForFrameRateOverride
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index f5b97c2..14583e3 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -316,10 +316,10 @@
bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; }
- // Return the display refresh rate divider to match the layer
+ // Return the display refresh rate divisor to match the layer
// frame rate, or 0 if the display refresh rate is not a multiple of the
// layer refresh rate.
- static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
+ static int getFrameRateDivisor(Fps displayFrameRate, Fps layerFrameRate);
// Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
// for an integer t.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 82ff2fa..1fa455a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -173,15 +173,15 @@
return basePeriod;
}
- const auto divider =
- scheduler::RefreshRateConfigs::getFrameRateDivider(refreshRateConfigs
+ const auto divisor =
+ scheduler::RefreshRateConfigs::getFrameRateDivisor(refreshRateConfigs
->getCurrentRefreshRate()
.getFps(),
*frameRate);
- if (divider <= 1) {
+ if (divisor <= 1) {
return basePeriod;
}
- return basePeriod * divider;
+ return basePeriod * divisor;
};
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 9c32b1f..f6c81c0 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -169,6 +169,7 @@
// The period is the vsync period from the current display configuration.
void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
void resync() EXCLUDES(mRefreshRateConfigsLock);
+ void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. periodFlushed will be true if
// VsyncController detected that the vsync period changed, and false otherwise.
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 61d2fb7..77782e9 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -256,7 +256,7 @@
/*
* Returns whether a given vsync timestamp is in phase with a frame rate.
- * If the frame rate is not a divider of the refresh rate, it is always considered in phase.
+ * If the frame rate is not a divisor of the refresh rate, it is always considered in phase.
* For example, if the vsync timestamps are (16.6,33.3,50.0,66.6):
* isVSyncInPhase(16.6, 30) = true
* isVSyncInPhase(33.3, 30) = false
@@ -271,42 +271,42 @@
};
std::lock_guard lock(mMutex);
- const auto divider =
- RefreshRateConfigs::getFrameRateDivider(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
- if (divider <= 1 || timePoint == 0) {
+ const auto divisor =
+ RefreshRateConfigs::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+ if (divisor <= 1 || timePoint == 0) {
return true;
}
const nsecs_t period = mRateMap[mIdealPeriod].slope;
const nsecs_t justBeforeTimePoint = timePoint - period / 2;
- const nsecs_t dividedPeriod = mIdealPeriod / divider;
+ const nsecs_t dividedPeriod = mIdealPeriod / divisor;
- // If this is the first time we have asked about this divider with the
+ // If this is the first time we have asked about this divisor with the
// current vsync period, it is considered in phase and we store the closest
// vsync timestamp
- const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod);
- if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) {
+ const auto knownTimestampIter = mRateDivisorKnownTimestampMap.find(dividedPeriod);
+ if (knownTimestampIter == mRateDivisorKnownTimestampMap.end()) {
const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
- mRateDividerKnownTimestampMap[dividedPeriod] = vsync;
+ mRateDivisorKnownTimestampMap[dividedPeriod] = vsync;
return true;
}
- // Find the next N vsync timestamp where N is the divider.
+ // Find the next N vsync timestamp where N is the divisor.
// One of these vsyncs will be in phase. We return the one which is
// the most aligned with the last known in phase vsync
- std::vector<VsyncError> vsyncs(static_cast<size_t>(divider));
+ std::vector<VsyncError> vsyncs(static_cast<size_t>(divisor));
const nsecs_t knownVsync = knownTimestampIter->second;
nsecs_t point = justBeforeTimePoint;
- for (size_t i = 0; i < divider; i++) {
+ for (size_t i = 0; i < divisor; i++) {
const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
- const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider);
+ const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divisor);
const auto error = std::abs(std::round(numPeriods) - numPeriods);
vsyncs[i] = {vsync, error};
point = vsync + 1;
}
const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
- mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
+ mRateDivisorKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index cfaf7d6..3181102 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -96,7 +96,7 @@
std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
// Map between the divided vsync period and the last known vsync timestamp
- std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex);
+ std::unordered_map<nsecs_t, nsecs_t> mutable mRateDivisorKnownTimestampMap GUARDED_BY(mMutex);
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc88793..aa5c00b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -480,6 +480,9 @@
property_get("debug.sf.disable_client_composition_cache", value, "0");
mDisableClientCompositionCache = atoi(value);
+ property_get("debug.sf.predict_hwc_composition_strategy", value, "0");
+ mPredictCompositionStrategy = atoi(value);
+
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
@@ -500,6 +503,8 @@
if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
mTransactionTracing.emplace();
}
+
+ mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -507,7 +512,7 @@
return LatchUnsignaledConfig::Always;
}
- if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
+ if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, true)) {
return LatchUnsignaledConfig::AutoSingleLayer;
}
@@ -1436,8 +1441,15 @@
status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) {
auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
- if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
- return getHwComposer().setBootDisplayMode(*displayId, id);
+ if (const auto displayDevice = getDisplayDeviceLocked(displayToken)) {
+ const auto mode = displayDevice->getMode(DisplayModeId{id});
+ if (mode == nullptr) {
+ ALOGE("%s: invalid display mode (%d)", __FUNCTION__, id);
+ return BAD_VALUE;
+ }
+
+ return getHwComposer().setBootDisplayMode(displayDevice->getPhysicalId(),
+ mode->getHwcId());
} else {
ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
return BAD_VALUE;
@@ -1957,8 +1969,8 @@
}
void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) {
- // TODO(b/198106220): force enable HWVsync to avoid drift problem during
- // idle.
+ ATRACE_CALL();
+ mScheduler->forceNextResync();
}
void SurfaceFlinger::setVsyncEnabled(bool enabled) {
@@ -2344,16 +2356,19 @@
void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats,
nsecs_t compositeToPresentLatency) {
+ // Avoid division by 0 by defaulting to 60Hz
+ const auto vsyncPeriod = stats.vsyncPeriod ?: (60_Hz).getPeriodNsecs();
+
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
- ? (stats.vsyncPeriod -
- (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % stats.vsyncPeriod))
- : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % stats.vsyncPeriod);
+ ? (vsyncPeriod -
+ (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % vsyncPeriod))
+ : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % vsyncPeriod);
// Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
- idealLatency = stats.vsyncPeriod;
+ idealLatency = vsyncPeriod;
}
// Snap the latency to a value that removes scheduling jitter from the
@@ -2362,19 +2377,30 @@
// something (such as user input) to an accurate diasplay time.
// Snapping also allows an app to precisely calculate
// mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
- const nsecs_t bias = stats.vsyncPeriod / 2;
- const int64_t extraVsyncs = (stats.vsyncPeriod) > 0 ?
- ((compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod) :
- 0;
+ const nsecs_t bias = vsyncPeriod / 2;
+ const int64_t extraVsyncs = ((compositeToPresentLatency - idealLatency + bias) / vsyncPeriod);
const nsecs_t snappedCompositeToPresentLatency =
- (extraVsyncs > 0) ? idealLatency + (extraVsyncs * stats.vsyncPeriod) : idealLatency;
+ (extraVsyncs > 0) ? idealLatency + (extraVsyncs * vsyncPeriod) : idealLatency;
std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency;
- getBE().mCompositorTiming.interval = stats.vsyncPeriod;
+ getBE().mCompositorTiming.interval = vsyncPeriod;
getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
}
+bool SurfaceFlinger::isHdrLayer(Layer* layer) const {
+ if (!isHdrDataspace(layer->getDataSpace())) {
+ return false;
+ }
+ if (mIgnoreHdrCameraLayers) {
+ auto buffer = layer->getBuffer();
+ if (buffer && (buffer->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
void SurfaceFlinger::postComposition() {
ATRACE_CALL();
ALOGV("postComposition");
@@ -2460,7 +2486,7 @@
mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
const auto layerFe = layer->getCompositionEngineLayerFE();
if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
- if (isHdrDataspace(layer->getDataSpace())) {
+ if (isHdrLayer(layer)) {
const auto* outputLayer =
compositionDisplay->getOutputLayerForLayer(layerFe);
if (outputLayer) {
@@ -3646,6 +3672,54 @@
return false;
}
+void SurfaceFlinger::flushPendingTransactionQueues(
+ std::vector<TransactionState>& transactions,
+ std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
+ std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
+ bool tryApplyUnsignaled) {
+ auto it = mPendingTransactionQueues.begin();
+ while (it != mPendingTransactionQueues.end()) {
+ auto& [applyToken, transactionQueue] = *it;
+ while (!transactionQueue.empty()) {
+ if (stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
+ ATRACE_NAME("stopTransactionProcessing");
+ break;
+ }
+
+ auto& transaction = transactionQueue.front();
+ const auto ready =
+ transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+ transaction.isAutoTimestamp,
+ transaction.desiredPresentTime,
+ transaction.originUid, transaction.states,
+ bufferLayersReadyToPresent, transactions.size(),
+ tryApplyUnsignaled);
+ ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+ if (ready == TransactionReadiness::NotReady) {
+ setTransactionFlags(eTransactionFlushNeeded);
+ break;
+ }
+ transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ bufferLayersReadyToPresent.insert(state.surface);
+ });
+ const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
+ if (appliedUnsignaled) {
+ applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
+ }
+
+ transactions.emplace_back(std::move(transaction));
+ transactionQueue.pop();
+ }
+
+ if (transactionQueue.empty()) {
+ it = mPendingTransactionQueues.erase(it);
+ mTransactionQueueCV.broadcast();
+ } else {
+ it = std::next(it, 1);
+ }
+ }
+}
+
bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
@@ -3658,63 +3732,25 @@
Mutex::Autolock _l(mStateLock);
{
Mutex::Autolock _l(mQueueLock);
- // Collect transactions from pending transaction queue.
- auto it = mPendingTransactionQueues.begin();
- while (it != mPendingTransactionQueues.end()) {
- auto& [applyToken, transactionQueue] = *it;
- while (!transactionQueue.empty()) {
- if (stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
- ATRACE_NAME("stopTransactionProcessing");
- break;
- }
- auto& transaction = transactionQueue.front();
- const auto ready =
- transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
- transaction.isAutoTimestamp,
- transaction.desiredPresentTime,
- transaction.originUid, transaction.states,
- bufferLayersReadyToPresent,
- transactions.size());
- ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
- if (ready == TransactionReadiness::NotReady) {
- setTransactionFlags(eTransactionFlushNeeded);
- break;
- }
- transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
- bufferLayersReadyToPresent.insert(state.surface);
- });
- const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
- if (appliedUnsignaled) {
- applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
- }
+ // First collect transactions from the pending transaction queues.
+ // We are not allowing unsignaled buffers here as we want to
+ // collect all the transactions from applyTokens that are ready first.
+ flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+ applyTokensWithUnsignaledTransactions,
+ /*tryApplyUnsignaled*/ false);
- transactions.emplace_back(std::move(transaction));
- transactionQueue.pop();
- }
-
- if (transactionQueue.empty()) {
- it = mPendingTransactionQueues.erase(it);
- mTransactionQueueCV.broadcast();
- } else {
- it = std::next(it, 1);
- }
- }
-
- // Collect transactions from current transaction queue or queue to pending transactions.
- // Case 1: push to pending when transactionIsReadyToBeApplied is false
- // or the first transaction was unsignaled.
- // Case 2: push to pending when there exist a pending queue.
- // Case 3: others are the transactions that are ready to apply.
+ // Second, collect transactions from the transaction queue.
+ // Here as well we are not allowing unsignaled buffers for the same
+ // reason as above.
while (!mTransactionQueue.empty()) {
auto& transaction = mTransactionQueue.front();
const bool pendingTransactions =
mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
const auto ready = [&]() REQUIRES(mStateLock) {
- if (pendingTransactions ||
- stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
- ATRACE_NAME("pendingTransactions || stopTransactionProcessing");
+ if (pendingTransactions) {
+ ATRACE_NAME("pendingTransactions");
return TransactionReadiness::NotReady;
}
@@ -3723,7 +3759,8 @@
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
bufferLayersReadyToPresent,
- transactions.size());
+ transactions.size(),
+ /*tryApplyUnsignaled*/ false);
}();
ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
if (ready == TransactionReadiness::NotReady) {
@@ -3732,16 +3769,21 @@
transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
bufferLayersReadyToPresent.insert(state.surface);
});
- const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
- if (appliedUnsignaled) {
- applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
- }
transactions.emplace_back(std::move(transaction));
}
mTransactionQueue.pop_front();
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
+ // We collected all transactions that could apply without latching unsignaled buffers.
+ // If we are allowing latch unsignaled of some form, now it's the time to go over the
+ // transactions that were not applied and try to apply them unsignaled.
+ if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
+ flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+ applyTokensWithUnsignaledTransactions,
+ /*tryApplyUnsignaled*/ true);
+ }
+
return applyTransactions(transactions, vsyncId);
}
}
@@ -3849,7 +3891,7 @@
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
uid_t originUid, const Vector<ComposerState>& states,
const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
- size_t totalTXapplied) const -> TransactionReadiness {
+ size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
// Do not present if the desiredPresentTime has not passed unless it is more than one second
@@ -3886,7 +3928,7 @@
continue;
}
- const bool allowLatchUnsignaled =
+ const bool allowLatchUnsignaled = tryApplyUnsignaled &&
shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
allowLatchUnsignaled ? "true" : "false");
@@ -5472,9 +5514,6 @@
case SET_FRAME_RATE:
case GET_DISPLAY_BRIGHTNESS_SUPPORT:
case GET_DISPLAY_DECORATION_SUPPORT:
- // captureLayers and captureDisplay will handle the permission check in the function
- case CAPTURE_LAYERS:
- case CAPTURE_DISPLAY:
case SET_FRAME_TIMELINE_INFO:
case GET_GPU_CONTEXT_PRIORITY:
case GET_MAX_ACQUIRED_BUFFER_COUNT: {
@@ -5509,8 +5548,7 @@
}
return OK;
}
- case ADD_TRANSACTION_TRACE_LISTENER:
- case CAPTURE_DISPLAY_BY_ID: {
+ case ADD_TRANSACTION_TRACE_LISTENER: {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
@@ -5540,6 +5578,11 @@
}
return PERMISSION_DENIED;
}
+ case CAPTURE_LAYERS:
+ case CAPTURE_DISPLAY:
+ case CAPTURE_DISPLAY_BY_ID:
+ LOG_FATAL("Deprecated opcode: %d", code);
+ return PERMISSION_DENIED;
}
// These codes are used for the IBinder protocol to either interrogate the recipient
@@ -7188,6 +7231,34 @@
mLayersAdded = true;
return true;
}
+
+// gui::ISurfaceComposer
+binder::Status SurfaceComposerAIDL::captureDisplay(
+ const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
+ status_t status = mFlinger->captureDisplay(args, captureListener);
+ return binder::Status::fromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::captureDisplayById(
+ int64_t displayId, const sp<IScreenCaptureListener>& captureListener) {
+ status_t status;
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int uid = ipc->getCallingUid();
+ if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
+ std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
+ status = mFlinger->captureDisplay(*id, captureListener);
+ } else {
+ status = PERMISSION_DENIED;
+ }
+ return binder::Status::fromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::captureLayers(
+ const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
+ status_t status = mFlinger->captureLayers(args, captureListener);
+ return binder::Status::fromStatusT(status);
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3ecce33..5829838 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -23,6 +23,7 @@
*/
#include <android-base/thread_annotations.h>
+#include <android/gui/BnSurfaceComposer.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <gui/BufferQueue.h>
@@ -106,9 +107,13 @@
class RenderArea;
class TimeStats;
class FrameTracer;
+class ScreenCapturer;
class WindowInfosListenerInvoker;
+using gui::CaptureArgs;
+using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
+using gui::LayerCaptureArgs;
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -339,6 +344,11 @@
void disableExpensiveRendering();
FloatRect getMaxDisplayBounds();
+ // If set, composition engine tries to predict the composition strategy provided by HWC
+ // based on the previous frame. If the strategy can be predicted, gpu composition will
+ // run parallel to the hwc validateDisplay call and re-run if the predition is incorrect.
+ bool mPredictCompositionStrategy = false;
+
protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
@@ -540,9 +550,9 @@
ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
- status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override;
- status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override;
- status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override;
+ status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+ status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
@@ -750,6 +760,12 @@
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
+ void flushPendingTransactionQueues(
+ std::vector<TransactionState>& transactions,
+ std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
+ std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
+ bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock);
+
uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
@@ -780,7 +796,7 @@
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
uid_t originUid, const Vector<ComposerState>& states,
const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
- size_t totalTXapplied) const REQUIRES(mStateLock);
+ size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock);
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
size_t totalTXapplied) const;
@@ -1120,6 +1136,8 @@
void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
REQUIRES(mStateLock);
+ bool isHdrLayer(Layer* layer) const;
+
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
pid_t mPid;
@@ -1168,6 +1186,7 @@
// Used to ensure we omit a callback when HDR layer info listener is newly added but the
// scene hasn't changed
bool mAddingHDRLayerInfoListener = false;
+ bool mIgnoreHdrCameraLayers = false;
// Set during transaction application stage to track if the input info or children
// for a layer has changed.
@@ -1401,6 +1420,22 @@
} mPowerHintSessionData GUARDED_BY(SF_MAIN_THREAD);
nsecs_t mAnimationTransactionTimeout = s2ns(5);
+
+ friend class SurfaceComposerAIDL;
+};
+
+class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
+public:
+ SurfaceComposerAIDL(sp<SurfaceFlinger> sf) { mFlinger = sf; }
+
+ binder::Status captureDisplay(const DisplayCaptureArgs&,
+ const sp<IScreenCaptureListener>&) override;
+ binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override;
+ binder::Status captureLayers(const LayerCaptureArgs&,
+ const sp<IScreenCaptureListener>&) override;
+
+private:
+ sp<SurfaceFlinger> mFlinger;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 16f6e31..20fa091 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,5 +371,9 @@
return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
}
+bool ignore_hdr_camera_layers(bool defaultValue) {
+ return SurfaceFlingerProperties::ignore_hdr_camera_layers().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 8d0e426..080feee 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -100,6 +100,8 @@
bool enable_sdr_dimming(bool defaultValue);
+bool ignore_hdr_camera_layers(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index 114f1eb..e8fe734 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_binary {
name: "layertracegenerator",
defaults: [
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 09ffb02..d504155 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -357,7 +357,7 @@
mFdp.ConsumeFloatingPoint<float>()),
Fps::fromValue(
mFdp.ConsumeFloatingPoint<float>()));
- RefreshRateConfigs::getFrameRateDivider(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+ RefreshRateConfigs::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
android::mock::TimeStats timeStats;
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 2e9e659..3598308 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -136,6 +136,8 @@
// Corner radius explicitly set on layer rather than inherited
float requested_corner_radius = 56;
+
+ RectProto destination_frame = 57;
}
message PositionProto {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index caeff4a..ec18054 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -152,6 +152,11 @@
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
+ // publish gui::ISurfaceComposer, the new AIDL interface
+ sp<SurfaceComposerAIDL> composerAIDL = new SurfaceComposerAIDL(flinger);
+ sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
+
startDisplayService(); // dependency on SF getting registered above
if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 7702ea2..bcbe21a 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -461,4 +461,13 @@
scope: Public
access: Readonly
prop_name: "ro.surface_flinger.enable_sdr_dimming"
+}
+
+# Ignores Camera layers when calculating HDR coverage information
+prop {
+ api_name: "ignore_hdr_camera_layers"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
}
\ 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 bf1e7e2..348a462 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -69,6 +69,10 @@
prop_name: "ro.surface_flinger.has_wide_color_display"
}
prop {
+ api_name: "ignore_hdr_camera_layers"
+ prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
+ }
+ prop {
api_name: "max_frame_buffer_acquired_buffers"
type: Long
prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index fa1a5ed..094b0ff 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -22,6 +22,8 @@
#include <gui/LayerState.h>
namespace android {
+using gui::DisplayCaptureArgs;
+using gui::LayerCaptureArgs;
using gui::ScreenCaptureResults;
namespace test {
@@ -40,11 +42,11 @@
args.grayscale = true;
Parcel p;
- args.write(p);
+ args.writeToParcel(&p);
p.setDataPosition(0);
DisplayCaptureArgs args2;
- args2.read(p);
+ args2.readFromParcel(&p);
ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
@@ -71,11 +73,11 @@
args.grayscale = true;
Parcel p;
- args.write(p);
+ args.writeToParcel(&p);
p.setDataPosition(0);
LayerCaptureArgs args2;
- args2.read(p);
+ args2.readFromParcel(&p);
ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
new file mode 100644
index 0000000..ff3e1c5
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "transactiontrace_testsuite",
+ defaults: [
+ "libsurfaceflinger_mocks_defaults",
+ "surfaceflinger_defaults",
+ "skia_renderengine_deps",
+ ],
+ test_suites: ["device-tests"],
+ sanitize: {
+ address: false,
+ },
+ srcs: [
+ ":libsurfaceflinger_sources",
+ ":libsurfaceflinger_mock_sources",
+ ":layertracegenerator_sources",
+ "TransactionTraceTestSuite.cpp",
+ ],
+ static_libs: [
+ "libc++fs",
+ ],
+ header_libs: [
+ "libsurfaceflinger_mocks_headers",
+ "layertracegenerator_headers",
+ ],
+ data: ["testdata/*"],
+}
diff --git a/services/surfaceflinger/tests/tracing/AndroidTest.xml b/services/surfaceflinger/tests/tracing/AndroidTest.xml
new file mode 100644
index 0000000..c0aeb35
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for transactiontrace_testsuite">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="transactiontrace_testsuite->/data/local/tmp/transactiontrace_testsuite" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="transactiontrace_testsuite" />
+ </test>
+</configuration>
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
new file mode 100644
index 0000000..ac4354c
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <LayerTraceGenerator.h>
+#include <Tracing/TransactionProtoParser.h>
+#include <layerproto/LayerProtoHeader.h>
+#include <log/log.h>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+class TransactionTraceTestSuite : public testing::Test,
+ public testing::WithParamInterface<std::filesystem::path> {
+public:
+ static std::vector<std::filesystem::path> sTransactionTraces;
+ static constexpr std::string_view sTransactionTracePrefix = "transactions_trace_";
+ static constexpr std::string_view sLayersTracePrefix = "layers_trace_";
+ static constexpr std::string_view sTracePostfix = ".winscope";
+
+ proto::TransactionTraceFile mTransactionTrace;
+ LayersTraceFileProto mExpectedLayersTraceProto;
+ LayersTraceFileProto mActualLayersTraceProto;
+
+protected:
+ void SetUp() override {
+ std::filesystem::path transactionTracePath = GetParam();
+ parseTransactionTraceFromFile(transactionTracePath.c_str(), mTransactionTrace);
+
+ std::string expectedLayersFilename = std::string(sLayersTracePrefix) +
+ transactionTracePath.filename().string().substr(sTransactionTracePrefix.length());
+ std::string expectedLayersTracePath =
+ transactionTracePath.parent_path().string() + "/" + expectedLayersFilename;
+ EXPECT_TRUE(std::filesystem::exists(std::filesystem::path(expectedLayersTracePath)));
+ parseLayersTraceFromFile(expectedLayersTracePath.c_str(), mExpectedLayersTraceProto);
+ TemporaryDir temp_dir;
+ std::string actualLayersTracePath =
+ std::string(temp_dir.path) + "/" + expectedLayersFilename + "_actual";
+
+ EXPECT_TRUE(
+ LayerTraceGenerator().generate(mTransactionTrace, actualLayersTracePath.c_str()))
+ << "Failed to generate layers trace from " << transactionTracePath;
+ EXPECT_TRUE(std::filesystem::exists(std::filesystem::path(actualLayersTracePath)));
+ parseLayersTraceFromFile(actualLayersTracePath.c_str(), mActualLayersTraceProto);
+ }
+
+ void parseTransactionTraceFromFile(const char* transactionTracePath,
+ proto::TransactionTraceFile& outProto) {
+ ALOGD("Parsing file %s...", transactionTracePath);
+ std::fstream input(transactionTracePath, std::ios::in | std::ios::binary);
+ EXPECT_TRUE(input) << "Error could not open " << transactionTracePath;
+ EXPECT_TRUE(outProto.ParseFromIstream(&input))
+ << "Failed to parse " << transactionTracePath;
+ }
+
+ void parseLayersTraceFromFile(const char* layersTracePath, LayersTraceFileProto& outProto) {
+ ALOGD("Parsing file %s...", layersTracePath);
+ std::fstream input(layersTracePath, std::ios::in | std::ios::binary);
+ EXPECT_TRUE(input) << "Error could not open " << layersTracePath;
+ EXPECT_TRUE(outProto.ParseFromIstream(&input)) << "Failed to parse " << layersTracePath;
+ }
+};
+
+std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{};
+
+TEST_P(TransactionTraceTestSuite, validateEndState) {
+ ASSERT_GT(mActualLayersTraceProto.entry_size(), 0);
+ ASSERT_GT(mExpectedLayersTraceProto.entry_size(), 0);
+
+ auto expectedLastEntry =
+ mExpectedLayersTraceProto.entry(mExpectedLayersTraceProto.entry_size() - 1);
+ auto actualLastEntry = mActualLayersTraceProto.entry(mActualLayersTraceProto.entry_size() - 1);
+
+ EXPECT_EQ(expectedLastEntry.layers().layers_size(), actualLastEntry.layers().layers_size());
+ for (int i = 0;
+ i < expectedLastEntry.layers().layers_size() && i < actualLastEntry.layers().layers_size();
+ i++) {
+ auto expectedLayer = expectedLastEntry.layers().layers(i);
+ auto actualLayer = actualLastEntry.layers().layers(i);
+ EXPECT_EQ(expectedLayer.id(), actualLayer.id());
+ EXPECT_EQ(expectedLayer.name(), actualLayer.name());
+ EXPECT_EQ(expectedLayer.parent(), actualLayer.parent());
+ EXPECT_EQ(expectedLayer.z(), actualLayer.z());
+ EXPECT_EQ(expectedLayer.curr_frame(), actualLayer.curr_frame());
+ ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayer.name().c_str(),
+ expectedLayer.id(), expectedLayer.parent(), expectedLayer.z(),
+ expectedLayer.curr_frame());
+ }
+}
+
+std::string PrintToStringParamName(const ::testing::TestParamInfo<std::filesystem::path>& info) {
+ const auto& prefix = android::TransactionTraceTestSuite::sTransactionTracePrefix;
+ const auto& postfix = android::TransactionTraceTestSuite::sTracePostfix;
+
+ const auto& filename = info.param.filename().string();
+ return filename.substr(prefix.length(), filename.length() - prefix.length() - postfix.length());
+}
+
+INSTANTIATE_TEST_CASE_P(TransactionTraceTestSuites, TransactionTraceTestSuite,
+ testing::ValuesIn(TransactionTraceTestSuite::sTransactionTraces),
+ PrintToStringParamName);
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ for (const auto& entry : std::filesystem::directory_iterator(
+ android::base::GetExecutableDirectory() + "/testdata/")) {
+ if (!entry.is_regular_file()) {
+ continue;
+ }
+ const auto& filename = entry.path().filename().string();
+ const auto& prefix = android::TransactionTraceTestSuite::sTransactionTracePrefix;
+ if (filename.compare(0, prefix.length(), prefix)) {
+ continue;
+ }
+ const std::string& path = entry.path().string();
+ const auto& postfix = android::TransactionTraceTestSuite::sTracePostfix;
+ if (path.compare(path.length() - postfix.length(), postfix.length(), postfix)) {
+ continue;
+ }
+ android::TransactionTraceTestSuite::sTransactionTraces.push_back(path);
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/tracing/readme.md b/services/surfaceflinger/tests/tracing/readme.md
new file mode 100644
index 0000000..3e80a74
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/readme.md
@@ -0,0 +1,20 @@
+### TransactionTrace Testsuite ###
+
+Hassle free way to test and validate whether a sequence of
+transactions will produce the expected front end state(s). Test
+runs through all testdata/transactions_trace_*.winscope files,
+generates layer states and checks if the states match the
+corresponding layer trace in testdata.
+
+
+#### Run Test ####
+`atest transactiontrace_testsuite`
+
+
+#### Workflow ####
+Add transaction traces that resulted in front end bugs along
+with the layer trace after fixing the issue. The layer trace
+can be generated by using the layertracegenerator tool. The
+main goal of this test suite is to add regression tests with
+minimal effort.
+
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
new file mode 100644
index 0000000..9e4005c
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
new file mode 100644
index 0000000..8356ae7
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 9143d61..97f3747 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1873,37 +1873,37 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
}
-TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
+TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) {
RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
const auto frameRate = 30_Hz;
Fps displayRefreshRate = configs.getCurrentRefreshRate().getFps();
- EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+ EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
configs.setCurrentModeId(kModeId60);
displayRefreshRate = configs.getCurrentRefreshRate().getFps();
- EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+ EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
configs.setCurrentModeId(kModeId72);
displayRefreshRate = configs.getCurrentRefreshRate().getFps();
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
configs.setCurrentModeId(kModeId90);
displayRefreshRate = configs.getCurrentRefreshRate().getFps();
- EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+ EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
configs.setCurrentModeId(kModeId120);
displayRefreshRate = configs.getCurrentRefreshRate().getFps();
- EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
configs.setCurrentModeId(kModeId90);
displayRefreshRate = configs.getCurrentRefreshRate().getFps();
- EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, 22.5_Hz));
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 25_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 23.976_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(30_Hz, 29.97_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(60_Hz, 59.94_Hz));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 23.976_Hz));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(30_Hz, 29.97_Hz));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(60_Hz, 59.94_Hz));
}
TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index eefa11f..46c8404 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -551,13 +551,52 @@
kExpectedTransactionsPending);
}
-TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemoveSignaledWithUnsignaledIntact) {
+TEST_F(LatchUnsignaledAutoSingleLayerTest,
+ UnsignaledNotAppliedWhenThereAreSignaled_UnsignaledFirst) {
const sp<IBinder> kApplyToken1 =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
+ const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 1u;
+ const auto kExpectedTransactionsApplied = 2u;
+ const auto kExpectedTransactionsPending = 1u;
+
+ const auto unsignaledTransaction =
+ createTransactionInfo(kApplyToken1,
+ {
+ createComposerState(kLayerId1,
+ fence(Fence::Status::Unsignaled),
+ layer_state_t::eBufferChanged),
+ });
+
+ const auto signaledTransaction =
+ createTransactionInfo(kApplyToken2,
+ {
+ createComposerState(kLayerId2,
+ fence(Fence::Status::Signaled),
+ layer_state_t::eBufferChanged),
+ });
+ const auto signaledTransaction2 =
+ createTransactionInfo(kApplyToken3,
+ {
+ createComposerState(kLayerId2,
+ fence(Fence::Status::Signaled),
+ layer_state_t::eBufferChanged),
+ });
+
+ setTransactionStates({unsignaledTransaction, signaledTransaction, signaledTransaction2},
+ kExpectedTransactionsApplied, kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) {
+ const sp<IBinder> kApplyToken1 =
+ IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
+ const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
+ const auto kLayerId1 = 1;
+ const auto kLayerId2 = 2;
+ const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 1u;
const auto signaledTransaction =
@@ -567,15 +606,23 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- const auto unsignaledTransaction =
+ const auto signaledTransaction2 =
createTransactionInfo(kApplyToken2,
{
+ createComposerState(kLayerId1,
+ fence(Fence::Status::Signaled),
+ layer_state_t::eBufferChanged),
+ });
+ const auto unsignaledTransaction =
+ createTransactionInfo(kApplyToken3,
+ {
createComposerState(kLayerId2,
fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+
+ setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction},
+ kExpectedTransactionsApplied, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 37ecd7c..74d2b7d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -464,16 +464,16 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
- const auto maxDivider = 5;
+ const auto maxDivisor = 5;
const auto maxPeriods = 15;
- for (int divider = 1; divider < maxDivider; divider++) {
+ for (int divisor = 1; divisor < maxDivisor; divisor++) {
for (int i = 0; i < maxPeriods; i++) {
- const bool expectedInPhase = (i % divider) == 0;
+ const bool expectedInPhase = (i % divisor) == 0;
EXPECT_THAT(expectedInPhase,
tracker.isVSyncInPhase(mNow + i * mPeriod - bias,
- Fps::fromPeriodNsecs(divider * mPeriod)))
+ Fps::fromPeriodNsecs(divisor * mPeriod)))
<< "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
- << (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
+ << (expectedInPhase ? "not " : "") << "in phase for divisor " << divisor;
}
}
}
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index cae7684..ee7e92c 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -16,6 +16,7 @@
#pragma once
#include <gui/SyncScreenCaptureListener.h>
+#include <private/gui/ComposerServiceAIDL.h>
#include <ui/Rect.h>
#include <utils/String8.h>
#include <functional>
@@ -31,15 +32,15 @@
public:
static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
ScreenCaptureResults& captureResults) {
- const auto sf = ComposerService::getComposerService();
+ const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
captureArgs.dataspace = ui::Dataspace::V0_SRGB;
const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t status = sf->captureDisplay(captureArgs, captureListener);
+ binder::Status status = sf->captureDisplay(captureArgs, captureListener);
- if (status != NO_ERROR) {
- return status;
+ if (status.transactionError() != NO_ERROR) {
+ return status.transactionError();
}
captureResults = captureListener->waitForResults();
return captureResults.result;
@@ -64,14 +65,14 @@
static status_t captureLayers(LayerCaptureArgs& captureArgs,
ScreenCaptureResults& captureResults) {
- const auto sf = ComposerService::getComposerService();
+ const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
captureArgs.dataspace = ui::Dataspace::V0_SRGB;
const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t status = sf->captureLayers(captureArgs, captureListener);
- if (status != NO_ERROR) {
- return status;
+ binder::Status status = sf->captureLayers(captureArgs, captureListener);
+ if (status.transactionError() != NO_ERROR) {
+ return status.transactionError();
}
captureResults = captureListener->waitForResults();
return captureResults.result;