Merge "sf-caching: Skip hole punch for layer that blends" into tm-dev
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7bde21..77bed0e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -41,6 +41,7 @@
#include <fstream>
#include <functional>
#include <regex>
+#include <unordered_set>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -81,6 +82,7 @@
#define GRANULAR_LOCKS
using android::base::ParseUint;
+using android::base::Split;
using android::base::StringPrintf;
using std::endl;
@@ -456,8 +458,8 @@
return res;
}
-static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
- if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid) {
+ if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
PLOG(ERROR) << "Failed to prepare " << path;
return -1;
}
@@ -597,9 +599,9 @@
}
}
-static binder::Status createAppDataDirs(const std::string& path,
- int32_t uid, int32_t* previousUid, int32_t cacheGid,
- const std::string& seInfo, mode_t targetMode) {
+static binder::Status createAppDataDirs(const std::string& path, int32_t uid, int32_t gid,
+ int32_t* previousUid, int32_t cacheGid,
+ const std::string& seInfo, mode_t targetMode) {
struct stat st{};
bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
@@ -623,9 +625,9 @@
}
// Prepare only the parent app directory
- if (prepare_app_dir(path, targetMode, uid) ||
- prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
- prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+ if (prepare_app_dir(path, targetMode, uid, gid) ||
+ prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+ prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
return error("Failed to prepare " + path);
}
@@ -684,7 +686,7 @@
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
- auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ auto status = createAppDataDirs(path, uid, uid, &previousUid, cacheGid, seInfo, targetMode);
if (!status.isOk()) {
return status;
}
@@ -709,7 +711,7 @@
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
- auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ auto status = createAppDataDirs(path, uid, uid, &previousUid, cacheGid, seInfo, targetMode);
if (!status.isOk()) {
return status;
}
@@ -722,72 +724,77 @@
}
}
- // TODO(b/220095381): Due to boot time regression, we have omitted call to
- // createAppDirectoryForSupplementalData 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);
- if (!status.isOk()) {
- return status;
+ if (flags & FLAG_STORAGE_SDK) {
+ // Safe to ignore status since we can retry creating this by calling reconcileSdkData
+ auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId,
+ previousAppId, seInfo, flags);
+ if (!ignore.isOk()) {
+ PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName;
}
+
+ } else {
+ // Package does not need sdk storage. Remove it.
+ destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
}
return ok();
}
/**
- * Responsible for creating /data/user/0/supplemental/<app-name> directory and other
+ * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<package-name> directory and other
* app level sub directories, such as ./shared
*/
-binder::Status InstalldNativeService::createAppDirectoryForSupplementalData(
+binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory(
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();
}
- // TODO(b/211763739): what if uuid is not nullptr or TEST?
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
- for (int i = 0; i < 2; i++) {
- int currentFlag = storageFlags[i];
+ for (int currentFlag : storageFlags) {
if ((flags & currentFlag) == 0) {
continue;
}
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());
- if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) {
- return error("Failed to prepare " + appPath);
+ auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+ packageName.c_str());
+#if SDK_DEBUG
+ LOG(DEBUG) << "Creating app-level sdk data directory: " << packagePath;
+#endif
+
+ if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM)) {
+ return error("Failed to prepare " + packagePath);
}
// 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, AID_NOBODY,
+ &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
}
@@ -837,6 +844,140 @@
return ok();
}
+binder::Status InstalldNativeService::reconcileSdkData(
+ const android::os::ReconcileSdkDataArgs& args) {
+ // Locking is performed depeer in the callstack.
+
+ return reconcileSdkData(args.uuid, args.packageName, args.sdkPackageNames, args.randomSuffixes,
+ args.userId, args.appId, args.previousAppId, args.seInfo, args.flags);
+}
+
+/**
+ * Reconciles per-sdk directory under app-level sdk data directory.
+
+ * E.g. `/data/misc_ce/0/sdksandbox/<package-name>/<sdkPackageName>-<randomSuffix>
+ *
+ * - If the sdk data package directory is missing, we create it first.
+ * - If sdkPackageNames is empty, we delete sdk package directory since it's not needed anymore.
+ * - If a sdk level directory we need to prepare already exist, we skip creating it again. This
+ * is to avoid having same per-sdk directory with different suffix.
+ * - If a sdk level directory exist which is absent from sdkPackageNames, we remove it.
+ */
+binder::Status InstalldNativeService::reconcileSdkData(
+ const std::optional<std::string>& uuid, const std::string& packageName,
+ const std::vector<std::string>& sdkPackageNames,
+ const std::vector<std::string>& randomSuffixes, int userId, int appId, int previousAppId,
+ const std::string& seInfo, int flags) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ for (const auto& sdkPackageName : sdkPackageNames) {
+ CHECK_ARGUMENT_PACKAGE_NAME(sdkPackageName);
+ }
+ LOCK_PACKAGE_USER();
+
+#if SDK_DEBUG
+ LOG(DEBUG) << "Creating per sdk data directory for: " << packageName;
+#endif
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ // Validate we have enough randomSuffixStrings
+ if (randomSuffixes.size() != sdkPackageNames.size()) {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ StringPrintf("Not enough random suffix. Required %d, received %d.",
+ (int)sdkPackageNames.size(), (int)randomSuffixes.size()));
+ }
+
+ // Prepare the sdk package directory in case it's missing
+ const auto status = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId,
+ previousAppId, seInfo, flags);
+ if (!status.isOk()) {
+ return status;
+ }
+
+ auto res = ok();
+ // We have to create sdk data for CE and DE storage
+ const int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int currentFlag : storageFlags) {
+ if ((flags & currentFlag) == 0) {
+ continue;
+ }
+ const bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+
+ // Since random suffix provided will be random every time, we need to ensure we don't end up
+ // creating multuple directories for same sdk package with different suffixes. This
+ // is ensured by fetching all the existing sub directories and storing them so that we can
+ // check for existence later. We also remove unconsumed sdk directories in this check.
+ const auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+ packageName.c_str());
+ const std::unordered_set<std::string> expectedSdkNames(sdkPackageNames.begin(),
+ sdkPackageNames.end());
+ // Store paths of per-sdk directory for sdk that already exists
+ std::unordered_map<std::string, std::string> sdkNamesThatExist;
+
+ const auto subDirHandler = [&packagePath, &expectedSdkNames, &sdkNamesThatExist,
+ &res](const std::string& filename) {
+ auto filepath = packagePath + "/" + filename;
+ auto tokens = Split(filename, "@");
+ if (tokens.size() != 2) {
+ // Not a per-sdk directory with random suffix
+ return;
+ }
+ auto sdkName = tokens[0];
+
+ // Remove the per-sdk directory if it is not referred in
+ // expectedSdkNames
+ if (expectedSdkNames.find(sdkName) == expectedSdkNames.end()) {
+ if (delete_dir_contents_and_dir(filepath) != 0) {
+ res = error("Failed to delete " + filepath);
+ return;
+ }
+ } else {
+ // Otherwise, store it as existing sdk level directory
+ sdkNamesThatExist[sdkName] = filepath;
+ }
+ };
+ const int ec = foreach_subdir(packagePath, subDirHandler);
+ if (ec != 0) {
+ res = error("Failed to process subdirs for " + packagePath);
+ continue;
+ }
+
+ // Create sdksandbox data directory for each sdksandbox package
+ for (int i = 0, size = sdkPackageNames.size(); i < size; i++) {
+ const std::string& sdkName = sdkPackageNames[i];
+ const std::string& randomSuffix = randomSuffixes[i];
+ std::string path;
+ if (const auto& it = sdkNamesThatExist.find(sdkName); it != sdkNamesThatExist.end()) {
+ // Already exists. Use existing path instead of creating a new one
+ path = it->second;
+ } else {
+ path = create_data_misc_sdk_sandbox_sdk_path(uuid_, isCeData, userId,
+ packageName.c_str(), sdkName.c_str(),
+ randomSuffix.c_str());
+ }
+
+ // Create the directory along with cache and code_cache
+ const 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 sdk data"));
+ }
+ const int32_t sandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
+ int32_t previousSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId);
+ auto status = createAppDataDirs(path, sandboxUid, AID_NOBODY, &previousSandboxUid,
+ cacheGid, seInfo, 0700);
+ if (!status.isOk()) {
+ res = status;
+ continue;
+ }
+ }
+ }
+
+ return res;
+}
+
binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
@@ -982,6 +1123,47 @@
}
}
}
+ auto status = clearSdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
+ if (!status.isOk()) {
+ res = status;
+ }
+ return res;
+}
+
+binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory(
+ const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+ int32_t flags) {
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+
+ binder::Status res = ok();
+ constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int i = 0; i < 2; i++) {
+ int currentFlag = storageFlags[i];
+ if ((flags & currentFlag) == 0) {
+ continue;
+ }
+ bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+ std::string suffix;
+ if (flags & FLAG_CLEAR_CACHE_ONLY) {
+ suffix = CACHE_DIR_POSTFIX;
+ } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+ suffix = CODE_CACHE_DIR_POSTFIX;
+ }
+
+ auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname);
+ if (access(appPath.c_str(), F_OK) != 0) continue;
+ const auto subDirHandler = [&appPath, &res, &suffix](const std::string& filename) {
+ auto filepath = appPath + "/" + filename + suffix;
+ if (delete_dir_contents(filepath, true) != 0) {
+ res = error("Failed to clear contents of " + filepath);
+ }
+ };
+ const int ec = foreach_subdir(appPath, subDirHandler);
+ if (ec != 0) {
+ res = error("Failed to process subdirs for " + appPath);
+ }
+ }
return res;
}
@@ -1078,6 +1260,32 @@
}
}
}
+ auto status = destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
+ if (!status.isOk()) {
+ res = status;
+ }
+ return res;
+}
+
+binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory(
+ const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+ int32_t flags) {
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+
+ binder::Status res = ok();
+ constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int i = 0; i < 2; i++) {
+ int currentFlag = storageFlags[i];
+ if ((flags & currentFlag) == 0) {
+ continue;
+ }
+ bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+ auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname);
+ if (rename_delete_dir_contents_and_dir(appPath) != 0) {
+ res = error("Failed to delete " + appPath);
+ }
+ }
return res;
}
@@ -1563,6 +1771,36 @@
}
}
+ // Copy sdk data for all known users
+ for (auto userId : users) {
+ LOCK_USER();
+
+ constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int currentFlag : storageFlags) {
+ const bool isCeData = currentFlag == FLAG_STORAGE_CE;
+
+ const auto from = create_data_misc_sdk_sandbox_package_path(from_uuid, isCeData, userId,
+ package_name);
+ if (access(from.c_str(), F_OK) != 0) {
+ LOG(INFO) << "Missing source " << from;
+ continue;
+ }
+ const auto to = create_data_misc_sdk_sandbox_path(to_uuid, isCeData, userId);
+
+ const int rc = copy_directory_recursive(from.c_str(), to.c_str());
+ if (rc != 0) {
+ res = error(rc, "Failed copying " + from + " to " + to);
+ goto fail;
+ }
+ }
+
+ if (!restoreconSdkDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+ appId, seInfo)
+ .isOk()) {
+ res = error("Failed to restorecon");
+ goto fail;
+ }
+ }
// We let the framework scan the new location and persist that before
// deleting the data in the old location; this ordering ensures that
// we can recover from things like battery pulls.
@@ -1590,6 +1828,18 @@
}
}
}
+ for (auto userId : users) {
+ LOCK_USER();
+ constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int currentFlag : storageFlags) {
+ const bool isCeData = currentFlag == FLAG_STORAGE_CE;
+ const auto to = create_data_misc_sdk_sandbox_package_path(to_uuid, isCeData, userId,
+ package_name);
+ if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
+ LOG(WARNING) << "Failed to rollback " << to;
+ }
+ }
+ }
return res;
}
@@ -1624,6 +1874,11 @@
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete " + path);
}
+ auto sdk_sandbox_de_path =
+ create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId);
+ if (delete_dir_contents_and_dir(sdk_sandbox_de_path, true) != 0) {
+ res = error("Failed to delete " + sdk_sandbox_de_path);
+ }
if (uuid_ == nullptr) {
path = create_data_misc_legacy_path(userId);
if (delete_dir_contents_and_dir(path, true) != 0) {
@@ -1640,6 +1895,11 @@
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete " + path);
}
+ auto sdk_sandbox_ce_path =
+ create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId);
+ if (delete_dir_contents_and_dir(sdk_sandbox_ce_path, true) != 0) {
+ res = error("Failed to delete " + sdk_sandbox_ce_path);
+ }
path = findDataMediaPath(uuid, userId);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete " + path);
@@ -2084,6 +2344,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);
}
@@ -2918,6 +3182,49 @@
return res;
}
+binder::Status InstalldNativeService::restoreconSdkDataLocked(
+ const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, const std::string& seInfo) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+
+ binder::Status res = ok();
+
+ // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
+ unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgName = packageName.c_str();
+ const char* seinfo = seInfo.c_str();
+
+ uid_t uid = multiuser_get_sdk_sandbox_uid(userId, appId);
+ constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int currentFlag : storageFlags) {
+ if ((flags & currentFlag) == 0) {
+ continue;
+ }
+ const bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+ const auto packagePath =
+ create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgName);
+ if (access(packagePath.c_str(), F_OK) != 0) {
+ LOG(INFO) << "Missing source " << packagePath;
+ continue;
+ }
+ const auto subDirHandler = [&packagePath, &seinfo, &uid, &seflags,
+ &res](const std::string& subDir) {
+ const auto& fullpath = packagePath + "/" + subDir;
+ if (selinux_android_restorecon_pkgdir(fullpath.c_str(), seinfo, uid, seflags) < 0) {
+ res = error("restorecon failed for " + fullpath);
+ }
+ };
+ const auto ec = foreach_subdir(packagePath, subDirHandler);
+ if (ec != 0) {
+ res = error("Failed to restorecon for subdirs of " + packagePath);
+ }
+ }
+ return res;
+}
+
binder::Status InstalldNativeService::createOatDir(const std::string& packageName,
const std::string& oatDir,
const std::string& instructionSet) {
@@ -3267,11 +3574,17 @@
if (flags & FLAG_STORAGE_CE) {
auto ce_path = create_data_user_ce_path(uuid_cstr, userId);
cleanup_invalid_package_dirs_under_path(ce_path);
+ auto sdksandbox_ce_path =
+ create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/true, userId);
+ cleanup_invalid_package_dirs_under_path(sdksandbox_ce_path);
}
if (flags & FLAG_STORAGE_DE) {
auto de_path = create_data_user_de_path(uuid_cstr, userId);
cleanup_invalid_package_dirs_under_path(de_path);
+ auto sdksandbox_de_path =
+ create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/false, userId);
+ cleanup_invalid_package_dirs_under_path(sdksandbox_de_path);
}
return ok();
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 6b4ba1f..1f0fc9c 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -58,12 +58,12 @@
const std::vector<android::os::CreateAppDataArgs>& args,
std::vector<android::os::CreateAppDataResult>* _aidl_return);
+ binder::Status reconcileSdkData(const android::os::ReconcileSdkDataArgs& args);
+
binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
- binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid,
- const std::string& packageName, int32_t userId,
- int32_t flags, int32_t appId, const std::string& seInfo);
+
binder::Status migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags);
binder::Status clearAppData(const std::optional<std::string>& uuid,
@@ -204,12 +204,30 @@
int32_t flags, int32_t appId, int32_t previousAppId,
const std::string& seInfo, int32_t targetSdkVersion,
int64_t* _aidl_return);
+ binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid,
+ const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, const std::string& seInfo);
- binder::Status createAppDirectoryForSupplementalData(const std::optional<std::string>& uuid,
+ binder::Status createSdkSandboxDataPackageDirectory(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 clearSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid,
+ const std::string& packageName,
+ int32_t userId, int32_t flags);
+ binder::Status destroySdkSandboxDataPackageDirectory(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 userId, int32_t flags);
+ binder::Status reconcileSdkData(const std::optional<std::string>& uuid,
+ const std::string& packageName,
+ const std::vector<std::string>& sdkPackageNames,
+ const std::vector<std::string>& randomSuffixes, int32_t userId,
+ int32_t appId, int32_t previousAppId, const std::string& seInfo,
+ int flags);
+ binder::Status restoreconSdkDataLocked(const std::optional<std::string>& uuid,
+ const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, const std::string& seInfo);
};
} // namespace installd
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index afedcc6..e08e9b6 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -24,6 +24,8 @@
android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
+ void reconcileSdkData(in android.os.ReconcileSdkDataArgs args);
+
void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, int flags, int appId, @utf8InCpp String seInfo);
void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
@@ -131,6 +133,7 @@
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
const int FLAG_STORAGE_EXTERNAL = 0x4;
+ const int FLAG_STORAGE_SDK = 0x8;
const int FLAG_CLEAR_CACHE_ONLY = 0x10;
const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20;
diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
new file mode 100644
index 0000000..2f794b1
--- /dev/null
+++ b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable ReconcileSdkDataArgs {
+ @nullable @utf8InCpp String uuid;
+ @utf8InCpp String packageName;
+ @utf8InCpp List<String> sdkPackageNames;
+ @utf8InCpp List<String> randomSuffixes;
+ int userId;
+ int appId;
+ int previousAppId;
+ @utf8InCpp String seInfo;
+ int flags;
+}
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/Android.bp b/cmds/installd/tests/Android.bp
index e390bab..b3baca5 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -26,6 +26,7 @@
"libasync_safe",
"libdiskusage",
"libext2_uuid",
+ "libgmock",
"libinstalld",
"liblog",
],
@@ -106,6 +107,7 @@
"libziparchive",
"liblog",
"liblogwrap",
+ "libc++fs",
],
test_config: "installd_service_test.xml",
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..04558d5 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -32,16 +32,20 @@
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
+#include <filesystem>
+#include <fstream>
#include <android/content/pm/IPackageManagerNative.h>
#include <binder/IServiceManager.h>
#include "InstalldNativeService.h"
+#include "binder/Status.h"
#include "binder_test_utils.h"
#include "dexopt.h"
#include "globals.h"
#include "utils.h"
using android::base::StringPrintf;
+namespace fs = std::filesystem;
namespace android {
std::string get_package_name(uid_t uid) {
@@ -75,13 +79,16 @@
namespace installd {
static constexpr const char* kTestUuid = "TEST";
-static constexpr const char* kTestPath = "/data/local/tmp/user/0";
+static constexpr const char* kTestPath = "/data/local/tmp";
+static constexpr const uid_t kNobodyUid = 9999;
static constexpr const uid_t kSystemUid = 1000;
static constexpr const int32_t kTestUserId = 0;
static constexpr const uid_t kTestAppId = 19999;
+static constexpr const int FLAG_STORAGE_SDK = InstalldNativeService::FLAG_STORAGE_SDK;
const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
-const uid_t kTestAppSupplementalUid = multiuser_get_supplemental_uid(kTestUserId, kTestAppId);
+const gid_t kTestCacheGid = multiuser_get_cache_gid(kTestUserId, kTestAppId);
+const uid_t kTestSdkSandboxUid = multiuser_get_sdk_sandbox_uid(kTestUserId, kTestAppId);
#define FLAG_FORCE InstalldNativeService::FLAG_FORCE
@@ -103,18 +110,18 @@
return create_cache_path_default(path, src, instruction_set);
}
-static std::string get_full_path(const char* path) {
- return StringPrintf("%s/%s", kTestPath, path);
+static std::string get_full_path(const std::string& path) {
+ return StringPrintf("%s/%s", kTestPath, path.c_str());
}
-static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
const std::string fullPath = get_full_path(path);
EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0);
EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0);
EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
}
-static int create(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
EXPECT_NE(fd, -1);
EXPECT_EQ(::fchown(fd, owner, group), 0);
@@ -122,8 +129,8 @@
return fd;
}
-static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
- EXPECT_EQ(::close(create(path, owner, group, mode)), 0);
+static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
+ EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0);
}
static int stat_gid(const char* path) {
@@ -138,7 +145,7 @@
return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
}
-static bool exists(const char* path) {
+static bool exists(const std::string& path) {
return ::access(get_full_path(path).c_str(), F_OK) == 0;
}
@@ -161,10 +168,11 @@
return result;
}
-static bool exists_renamed_deleted_dir() {
- return find_file(kTestPath, [](const std::string& name, bool is_dir) {
- return is_dir && is_renamed_deleted_dir(name);
- });
+static bool exists_renamed_deleted_dir(const std::string& rootDirectory) {
+ return find_file((std::string(kTestPath) + rootDirectory).c_str(),
+ [](const std::string& name, bool is_dir) {
+ return is_dir && is_renamed_deleted_dir(name);
+ });
}
class ServiceTest : public testing::Test {
@@ -179,197 +187,205 @@
service = new InstalldNativeService();
testUuid = kTestUuid;
system("rm -rf /data/local/tmp/user");
+ system("rm -rf /data/local/tmp/misc_ce");
+ system("rm -rf /data/local/tmp/misc_de");
system("mkdir -p /data/local/tmp/user/0");
-
+ system("mkdir -p /data/local/tmp/misc_ce/0/sdksandbox");
+ system("mkdir -p /data/local/tmp/misc_de/0/sdksandbox");
init_globals_from_data_and_root();
}
virtual void TearDown() {
delete service;
system("rm -rf /data/local/tmp/user");
+ system("rm -rf /data/local/tmp/misc_ce");
+ system("rm -rf /data/local/tmp/misc_de");
}
};
TEST_F(ServiceTest, FixupAppData_Upgrade) {
LOG(INFO) << "FixupAppData_Upgrade";
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/normal", 10000, 10000, 0700);
- mkdir("com.example/cache", 10000, 10000, 0700);
- touch("com.example/cache/file", 10000, 10000, 0700);
+ mkdir("user/0/com.example", 10000, 10000, 0700);
+ mkdir("user/0/com.example/normal", 10000, 10000, 0700);
+ mkdir("user/0/com.example/cache", 10000, 10000, 0700);
+ touch("user/0/com.example/cache/file", 10000, 10000, 0700);
service->fixupAppData(testUuid, 0);
- EXPECT_EQ(10000, stat_gid("com.example/normal"));
- EXPECT_EQ(20000, stat_gid("com.example/cache"));
- EXPECT_EQ(20000, stat_gid("com.example/cache/file"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/normal"));
+ EXPECT_EQ(20000, stat_gid("user/0/com.example/cache"));
+ EXPECT_EQ(20000, stat_gid("user/0/com.example/cache/file"));
- EXPECT_EQ(0700, stat_mode("com.example/normal"));
- EXPECT_EQ(02771, stat_mode("com.example/cache"));
- EXPECT_EQ(0700, stat_mode("com.example/cache/file"));
+ EXPECT_EQ(0700, stat_mode("user/0/com.example/normal"));
+ EXPECT_EQ(02771, stat_mode("user/0/com.example/cache"));
+ EXPECT_EQ(0700, stat_mode("user/0/com.example/cache/file"));
}
TEST_F(ServiceTest, FixupAppData_Moved) {
LOG(INFO) << "FixupAppData_Moved";
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/foo", 10000, 10000, 0700);
- touch("com.example/foo/file", 10000, 20000, 0700);
- mkdir("com.example/bar", 10000, 20000, 0700);
- touch("com.example/bar/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example", 10000, 10000, 0700);
+ mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+ touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+ touch("user/0/com.example/bar/file", 10000, 20000, 0700);
service->fixupAppData(testUuid, 0);
- EXPECT_EQ(10000, stat_gid("com.example/foo"));
- EXPECT_EQ(20000, stat_gid("com.example/foo/file"));
- EXPECT_EQ(10000, stat_gid("com.example/bar"));
- EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/foo"));
+ EXPECT_EQ(20000, stat_gid("user/0/com.example/foo/file"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/bar"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file"));
service->fixupAppData(testUuid, FLAG_FORCE);
- EXPECT_EQ(10000, stat_gid("com.example/foo"));
- EXPECT_EQ(10000, stat_gid("com.example/foo/file"));
- EXPECT_EQ(10000, stat_gid("com.example/bar"));
- EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/foo"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/foo/file"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/bar"));
+ EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file"));
}
TEST_F(ServiceTest, DestroyUserData) {
LOG(INFO) << "DestroyUserData";
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/foo", 10000, 10000, 0700);
- touch("com.example/foo/file", 10000, 20000, 0700);
- mkdir("com.example/bar", 10000, 20000, 0700);
- touch("com.example/bar/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example", 10000, 10000, 0700);
+ mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+ touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+ touch("user/0/com.example/bar/file", 10000, 20000, 0700);
- EXPECT_TRUE(exists("com.example/foo"));
- EXPECT_TRUE(exists("com.example/foo/file"));
- EXPECT_TRUE(exists("com.example/bar"));
- EXPECT_TRUE(exists("com.example/bar/file"));
+ EXPECT_TRUE(exists("user/0/com.example/foo"));
+ EXPECT_TRUE(exists("user/0/com.example/foo/file"));
+ EXPECT_TRUE(exists("user/0/com.example/bar"));
+ EXPECT_TRUE(exists("user/0/com.example/bar/file"));
service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE);
- EXPECT_FALSE(exists("com.example/foo"));
- EXPECT_FALSE(exists("com.example/foo/file"));
- EXPECT_FALSE(exists("com.example/bar"));
- EXPECT_FALSE(exists("com.example/bar/file"));
+ EXPECT_FALSE(exists("user/0/com.example/foo"));
+ EXPECT_FALSE(exists("user/0/com.example/foo/file"));
+ EXPECT_FALSE(exists("user/0/com.example/bar"));
+ EXPECT_FALSE(exists("user/0/com.example/bar/file"));
- EXPECT_FALSE(exists_renamed_deleted_dir());
+ EXPECT_FALSE(exists_renamed_deleted_dir("/user/0"));
}
TEST_F(ServiceTest, DestroyAppData) {
LOG(INFO) << "DestroyAppData";
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/foo", 10000, 10000, 0700);
- touch("com.example/foo/file", 10000, 20000, 0700);
- mkdir("com.example/bar", 10000, 20000, 0700);
- touch("com.example/bar/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example", 10000, 10000, 0700);
+ mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+ touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+ touch("user/0/com.example/bar/file", 10000, 20000, 0700);
- EXPECT_TRUE(exists("com.example/foo"));
- EXPECT_TRUE(exists("com.example/foo/file"));
- EXPECT_TRUE(exists("com.example/bar"));
- EXPECT_TRUE(exists("com.example/bar/file"));
+ EXPECT_TRUE(exists("user/0/com.example/foo"));
+ EXPECT_TRUE(exists("user/0/com.example/foo/file"));
+ EXPECT_TRUE(exists("user/0/com.example/bar"));
+ EXPECT_TRUE(exists("user/0/com.example/bar/file"));
service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0);
- EXPECT_FALSE(exists("com.example/foo"));
- EXPECT_FALSE(exists("com.example/foo/file"));
- EXPECT_FALSE(exists("com.example/bar"));
- EXPECT_FALSE(exists("com.example/bar/file"));
+ EXPECT_FALSE(exists("user/0/com.example/foo"));
+ EXPECT_FALSE(exists("user/0/com.example/foo/file"));
+ EXPECT_FALSE(exists("user/0/com.example/bar"));
+ EXPECT_FALSE(exists("user/0/com.example/bar/file"));
- EXPECT_FALSE(exists_renamed_deleted_dir());
+ EXPECT_FALSE(exists_renamed_deleted_dir("/user/0"));
}
TEST_F(ServiceTest, CleanupInvalidPackageDirs) {
LOG(INFO) << "CleanupInvalidPackageDirs";
- mkdir("5b14b6458a44==deleted==", 10000, 10000, 0700);
- mkdir("5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
- touch("5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
- mkdir("5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
- touch("5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
+ std::string rootDirectoryPrefix[] = {"user/0", "misc_ce/0/sdksandbox", "misc_de/0/sdksandbox"};
+ for (auto& prefix : rootDirectoryPrefix) {
+ mkdir(prefix + "/5b14b6458a44==deleted==", 10000, 10000, 0700);
+ mkdir(prefix + "/5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
+ touch(prefix + "/5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
+ mkdir(prefix + "/5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
+ touch(prefix + "/5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
- auto fd = create("5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
+ auto fd = create(prefix + "/5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
- mkdir("b14b6458a44NOTdeleted", 10000, 10000, 0700);
- mkdir("b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
- touch("b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
- mkdir("b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
- touch("b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
+ mkdir(prefix + "/b14b6458a44NOTdeleted", 10000, 10000, 0700);
+ mkdir(prefix + "/b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
+ touch(prefix + "/b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
+ mkdir(prefix + "/b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
+ touch(prefix + "/b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/foo", 10000, 10000, 0700);
- touch("com.example/foo/file", 10000, 20000, 0700);
- mkdir("com.example/bar", 10000, 20000, 0700);
- touch("com.example/bar/file", 10000, 20000, 0700);
+ mkdir(prefix + "/com.example", 10000, 10000, 0700);
+ mkdir(prefix + "/com.example/foo", 10000, 10000, 0700);
+ touch(prefix + "/com.example/foo/file", 10000, 20000, 0700);
+ mkdir(prefix + "/com.example/bar", 10000, 20000, 0700);
+ touch(prefix + "/com.example/bar/file", 10000, 20000, 0700);
- mkdir("==deleted==", 10000, 10000, 0700);
- mkdir("==deleted==/foo", 10000, 10000, 0700);
- touch("==deleted==/foo/file", 10000, 20000, 0700);
- mkdir("==deleted==/bar", 10000, 20000, 0700);
- touch("==deleted==/bar/file", 10000, 20000, 0700);
+ mkdir(prefix + "/==deleted==", 10000, 10000, 0700);
+ mkdir(prefix + "/==deleted==/foo", 10000, 10000, 0700);
+ touch(prefix + "/==deleted==/foo/file", 10000, 20000, 0700);
+ mkdir(prefix + "/==deleted==/bar", 10000, 20000, 0700);
+ touch(prefix + "/==deleted==/bar/file", 10000, 20000, 0700);
- EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo"));
- EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo/file"));
- EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar"));
- EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/file"));
- EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+ EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo"));
+ EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo/file"));
+ EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar"));
+ EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/file"));
+ EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file"));
- EXPECT_TRUE(exists("com.example/foo"));
- EXPECT_TRUE(exists("com.example/foo/file"));
- EXPECT_TRUE(exists("com.example/bar"));
- EXPECT_TRUE(exists("com.example/bar/file"));
+ EXPECT_TRUE(exists(prefix + "/com.example/foo"));
+ EXPECT_TRUE(exists(prefix + "/com.example/foo/file"));
+ EXPECT_TRUE(exists(prefix + "/com.example/bar"));
+ EXPECT_TRUE(exists(prefix + "/com.example/bar/file"));
- EXPECT_TRUE(exists("==deleted==/foo"));
- EXPECT_TRUE(exists("==deleted==/foo/file"));
- EXPECT_TRUE(exists("==deleted==/bar"));
- EXPECT_TRUE(exists("==deleted==/bar/file"));
+ EXPECT_TRUE(exists(prefix + "/==deleted==/foo"));
+ EXPECT_TRUE(exists(prefix + "/==deleted==/foo/file"));
+ EXPECT_TRUE(exists(prefix + "/==deleted==/bar"));
+ EXPECT_TRUE(exists(prefix + "/==deleted==/bar/file"));
- EXPECT_TRUE(exists_renamed_deleted_dir());
+ EXPECT_TRUE(exists_renamed_deleted_dir("/" + prefix));
- service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
+ service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
- EXPECT_EQ(::close(fd), 0);
+ EXPECT_EQ(::close(fd), 0);
- EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo"));
- EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo/file"));
- EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar"));
- EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/file"));
- EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+ EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo"));
+ EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo/file"));
+ EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar"));
+ EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/file"));
+ EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
- EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar"));
+ EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file"));
- EXPECT_TRUE(exists("com.example/foo"));
- EXPECT_TRUE(exists("com.example/foo/file"));
- EXPECT_TRUE(exists("com.example/bar"));
- EXPECT_TRUE(exists("com.example/bar/file"));
+ EXPECT_TRUE(exists(prefix + "/com.example/foo"));
+ EXPECT_TRUE(exists(prefix + "/com.example/foo/file"));
+ EXPECT_TRUE(exists(prefix + "/com.example/bar"));
+ EXPECT_TRUE(exists(prefix + "/com.example/bar/file"));
- EXPECT_FALSE(exists("==deleted==/foo"));
- EXPECT_FALSE(exists("==deleted==/foo/file"));
- EXPECT_FALSE(exists("==deleted==/bar"));
- EXPECT_FALSE(exists("==deleted==/bar/file"));
+ EXPECT_FALSE(exists(prefix + "/==deleted==/foo"));
+ EXPECT_FALSE(exists(prefix + "/==deleted==/foo/file"));
+ EXPECT_FALSE(exists(prefix + "/==deleted==/bar"));
+ EXPECT_FALSE(exists(prefix + "/==deleted==/bar/file"));
- EXPECT_FALSE(exists_renamed_deleted_dir());
+ EXPECT_FALSE(exists_renamed_deleted_dir(prefix));
+ }
}
TEST_F(ServiceTest, HashSecondaryDex) {
LOG(INFO) << "HashSecondaryDex";
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/foo", 10000, 10000, 0700);
- touch("com.example/foo/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example", 10000, 10000, 0700);
+ mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+ touch("user/0/com.example/foo/file", 10000, 20000, 0700);
std::vector<uint8_t> result;
- std::string dexPath = get_full_path("com.example/foo/file");
+ std::string dexPath = get_full_path("user/0/com.example/foo/file");
EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
@@ -389,7 +405,7 @@
LOG(INFO) << "HashSecondaryDex_NoSuch";
std::vector<uint8_t> result;
- std::string dexPath = get_full_path("com.example/foo/file");
+ std::string dexPath = get_full_path("user/0/com.example/foo/file");
EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
@@ -399,12 +415,12 @@
TEST_F(ServiceTest, HashSecondaryDex_Unreadable) {
LOG(INFO) << "HashSecondaryDex_Unreadable";
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/foo", 10000, 10000, 0700);
- touch("com.example/foo/file", 10000, 20000, 0300);
+ mkdir("user/0/com.example", 10000, 10000, 0700);
+ mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+ touch("user/0/com.example/foo/file", 10000, 20000, 0300);
std::vector<uint8_t> result;
- std::string dexPath = get_full_path("com.example/foo/file");
+ std::string dexPath = get_full_path("user/0/com.example/foo/file");
EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
@@ -414,12 +430,12 @@
TEST_F(ServiceTest, HashSecondaryDex_WrongApp) {
LOG(INFO) << "HashSecondaryDex_WrongApp";
- mkdir("com.example", 10000, 10000, 0700);
- mkdir("com.example/foo", 10000, 10000, 0700);
- touch("com.example/foo/file", 10000, 20000, 0700);
+ mkdir("user/0/com.example", 10000, 10000, 0700);
+ mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+ touch("user/0/com.example/foo/file", 10000, 20000, 0700);
std::vector<uint8_t> result;
- std::string dexPath = get_full_path("com.example/foo/file");
+ std::string dexPath = get_full_path("user/0/com.example/foo/file");
EXPECT_BINDER_FAIL(service->hashSecondaryDexFile(
dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result));
}
@@ -491,6 +507,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,28 +965,48 @@
"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) {
+ void CheckFileAccess(const std::string& path, uid_t uid, gid_t gid, mode_t mode) {
const auto fullPath = "/data/local/tmp/" + path;
ASSERT_TRUE(exists(fullPath.c_str())) << "For path: " << fullPath;
struct stat st;
ASSERT_EQ(0, stat(fullPath.c_str(), &st));
ASSERT_EQ(uid, st.st_uid) << "For path: " << fullPath;
- ASSERT_EQ(uid, st.st_gid) << "For path: " << fullPath;
+ ASSERT_EQ(gid, st.st_gid) << "For path: " << fullPath;
ASSERT_EQ(mode, st.st_mode) << "For path: " << fullPath;
}
bool exists(const char* path) { return ::access(path, F_OK) == 0; }
// Creates a default CreateAppDataArgs object
- android::os::CreateAppDataArgs createAppDataArgs() {
+ android::os::CreateAppDataArgs createAppDataArgs(const std::string& packageName) {
android::os::CreateAppDataArgs args;
args.uuid = kTestUuid;
- args.packageName = "com.foo";
+ args.packageName = packageName;
args.userId = kTestUserId;
args.appId = kTestAppId;
args.seInfo = "default";
+ args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
+ return args;
+ }
+
+ android::os::ReconcileSdkDataArgs reconcileSdkDataArgs(
+ std::string packageName, std::vector<std::string> codeNames,
+ std::vector<std::string> randomSuffixes) {
+ android::os::ReconcileSdkDataArgs args;
+ args.uuid = kTestUuid;
+ args.packageName = packageName;
+ for (const auto& codeName : codeNames) {
+ args.sdkPackageNames.push_back(codeName);
+ }
+ for (const auto& randomSuffix : randomSuffixes) {
+ args.randomSuffixes.push_back(randomSuffix);
+ }
+ args.userId = kTestUserId;
+ args.appId = kTestAppId;
+ args.previousAppId = -1;
+ args.seInfo = "default";
args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
return args;
}
@@ -973,8 +1022,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();
}
@@ -986,69 +1035,391 @@
private:
void clearAppData() {
+ ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user", true));
ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_ce", true));
ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_de", true));
- ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
}
};
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData) {
android::os::CreateAppDataResult result;
- android::os::CreateAppDataArgs args = createAppDataArgs();
- args.packageName = "com.foo";
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+ const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo";
+ CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
+ CheckFileAccess(fooCePath + "/shared", kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+ CheckFileAccess(fooCePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+ CheckFileAccess(fooCePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+
+ const std::string fooDePath = "misc_de/0/sdksandbox/com.foo";
+ CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
+ CheckFileAccess(fooDePath + "/shared", kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+ CheckFileAccess(fooDePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+ CheckFileAccess(fooDePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+}
+
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
// 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,
- S_IFDIR | S_ISGID | 0771);
- CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
- 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,
- S_IFDIR | S_ISGID | 0771);
- CheckFileAccess("misc_de/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
- S_IFDIR | S_ISGID | 0771);
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
}
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlagDeletesExisting) {
android::os::CreateAppDataResult result;
- android::os::CreateAppDataArgs args = createAppDataArgs();
- args.packageName = "com.foo";
- args.flags = FLAG_STORAGE_CE;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+ ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+
+ args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutDeFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_SDK;
// Create the app user data.
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, 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_CreatesSdkAppLevelData_WithoutCeFlag) {
android::os::CreateAppDataResult result;
- android::os::CreateAppDataArgs args = createAppDataArgs();
- args.packageName = "com.foo";
- args.flags = FLAG_STORAGE_DE;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
// Create the app user data.
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, kSystemUid, S_IFDIR | 0751);
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData) {
+ android::os::ReconcileSdkDataArgs args =
+ reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+ // Create the sdk data.
+ ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+ const std::string barCePath = "misc_ce/0/sdksandbox/com.foo/bar@random1";
+ CheckFileAccess(barCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+ CheckFileAccess(barCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+ CheckFileAccess(barCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+
+ const std::string bazCePath = "misc_ce/0/sdksandbox/com.foo/baz@random2";
+ CheckFileAccess(bazCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+ CheckFileAccess(bazCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+ CheckFileAccess(bazCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+
+ const std::string barDePath = "misc_de/0/sdksandbox/com.foo/bar@random1";
+ CheckFileAccess(barDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+ CheckFileAccess(barDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+ CheckFileAccess(barDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+
+ const std::string bazDePath = "misc_de/0/sdksandbox/com.foo/baz@random2";
+ CheckFileAccess(bazDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+ CheckFileAccess(bazDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+ CheckFileAccess(bazDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+ S_IFDIR | S_ISGID | 0771);
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_PackageNameCannotUseRandomSuffixSeparator) {
+ android::os::ReconcileSdkDataArgs args =
+ reconcileSdkDataArgs("com.foo", {"bar@illegal"}, {"random1"});
+
+ // Create the sdksandbox data.
+ auto status = service->reconcileSdkData(args);
+ ASSERT_EQ(status.exceptionCode(), binder::Status::EX_ILLEGAL_ARGUMENT);
+ ASSERT_EQ(status.exceptionMessage(), "Package name bar@illegal is malformed");
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_NotEnoughRandomSuffix) {
+ android::os::ReconcileSdkDataArgs args =
+ reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1"});
+
+ // Create the sdksandbox data.
+ auto status = service->reconcileSdkData(args);
+ ASSERT_EQ(status.exceptionCode(), binder::Status::EX_ILLEGAL_ARGUMENT);
+ ASSERT_EQ(status.exceptionMessage(), "Not enough random suffix. Required 2, received 1.");
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_DirectoryNotCreatedIfAlreadyExistsIgnoringSuffix) {
+ android::os::ReconcileSdkDataArgs args =
+ reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+ // Create the sdksandbox data.
+ ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+ // Retry with different random suffix
+ args.randomSuffixes[0] = "r10";
+ args.randomSuffixes[1] = "r20";
+
+ // Create the sdksandbox data again
+ ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+ // Previous directories from first attempt should exist
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar@random1", kTestSdkSandboxUid, kNobodyUid,
+ S_IFDIR | 0700);
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid,
+ S_IFDIR | 0700);
+ // No new directories should be created on second attempt
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@r10"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo/bar@r20"));
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_ExtraCodeDirectoriesAreDeleted) {
+ android::os::ReconcileSdkDataArgs args =
+ reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+ // Create the sdksandbox data.
+ ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+ // Retry with different package name
+ args.sdkPackageNames[0] = "bar.diff";
+
+ // Create the sdksandbox data again
+ ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+ // New directoris should exist
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar.diff@random1", kTestSdkSandboxUid, kNobodyUid,
+ S_IFDIR | 0700);
+ CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid,
+ S_IFDIR | 0700);
+ // Directory for old unreferred sdksandbox package name should be removed
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@random1"));
+}
+
+class DestroyAppDataTest : public SdkSandboxDataTest {};
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithCeAndDeFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ // Destroy the app user data.
+ ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+ args.flags, result.ceDataInode));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutDeFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ // Destroy the app user data.
+ ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_CE, result.ceDataInode));
+ ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+}
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutCeFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ // Destroy the app user data.
+ ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_DE, result.ceDataInode));
+ ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+class ClearAppDataTest : public SdkSandboxDataTest {
+public:
+ void createTestSdkData(const std::string& packageName, std::vector<std::string> sdkNames) {
+ const auto& cePackagePath = "/data/local/tmp/misc_ce/0/sdksandbox/" + packageName;
+ const auto& dePackagePath = "/data/local/tmp/misc_de/0/sdksandbox/" + packageName;
+ ASSERT_TRUE(mkdirs(cePackagePath + "/shared/cache", 0700));
+ ASSERT_TRUE(mkdirs(cePackagePath + "shared/code_cache", 0700));
+ ASSERT_TRUE(mkdirs(dePackagePath + "/shared/cache", 0700));
+ ASSERT_TRUE(mkdirs(dePackagePath + "/shared/code_cache", 0700));
+ std::ofstream{cePackagePath + "/shared/cache/cachedTestData.txt"};
+ for (auto sdkName : sdkNames) {
+ ASSERT_TRUE(mkdirs(cePackagePath + "/" + sdkName + "/cache", 0700));
+ ASSERT_TRUE(mkdirs(dePackagePath + "/" + sdkName + "/cache", 0700));
+ ASSERT_TRUE(mkdirs(cePackagePath + "/" + sdkName + "/code_cache", 0700));
+ ASSERT_TRUE(mkdirs(dePackagePath + "/" + sdkName + "/code_cache", 0700));
+ std::ofstream{cePackagePath + "/" + sdkName + "/cache/cachedTestData.txt"};
+ std::ofstream{cePackagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"};
+ std::ofstream{dePackagePath + "/" + sdkName + "/cache/cachedTestData.txt"};
+ std::ofstream{dePackagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"};
+ }
+ }
+};
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCacheFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ createTestSdkData("com.foo", {"sdk1", "sdk2"});
+ // Clear the app user data.
+ ASSERT_BINDER_SUCCESS(
+ service->clearAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_CE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY),
+ result.ceDataInode));
+ ASSERT_TRUE(
+ fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared/cache")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1/cache")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2/cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCodeCacheFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ createTestSdkData("com.foo", {"sdk1", "sdk2"});
+ // Clear the app user data.
+ ASSERT_BINDER_SUCCESS(
+ service->clearAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_CE |
+ (InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY),
+ result.ceDataInode));
+ ASSERT_TRUE(fs::is_empty(
+ fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared/code_cache")));
+ ASSERT_TRUE(
+ fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1/code_cache")));
+ ASSERT_TRUE(
+ fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2/code_cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCacheFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ createTestSdkData("com.foo", {"sdk1", "sdk2"});
+ // Clear the app user data
+ ASSERT_BINDER_SUCCESS(
+ service->clearAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_DE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY),
+ result.ceDataInode));
+ ASSERT_TRUE(
+ fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared/cache")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1/cache")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2/cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCodeCacheFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ createTestSdkData("com.foo", {"sdk1", "sdk2"});
+ // Clear the app user data.
+ ASSERT_BINDER_SUCCESS(
+ service->clearAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_DE |
+ (InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY),
+ result.ceDataInode));
+ ASSERT_TRUE(fs::is_empty(
+ fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared/code_cache")));
+ ASSERT_TRUE(
+ fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1/code_cache")));
+ ASSERT_TRUE(
+ fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2/code_cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndWithoutAnyCacheFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ createTestSdkData("com.foo", {"sdk1", "sdk2"});
+ // Clear the app user data.
+ ASSERT_BINDER_SUCCESS(service->clearAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_CE, result.ceDataInode));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndWithoutAnyCacheFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ createTestSdkData("com.foo", {"sdk1", "sdk2"});
+ // Clear the app user data.
+ ASSERT_BINDER_SUCCESS(service->clearAppData(args.uuid, args.packageName, args.userId,
+ FLAG_STORAGE_DE, result.ceDataInode));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1")));
+ ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2")));
+}
+
+class DestroyUserDataTest : public SdkSandboxDataTest {};
+
+TEST_F(DestroyUserDataTest, DestroySdkData_WithCeFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ // Destroy user data
+ ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_CE));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox"));
+ ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox"));
+}
+
+TEST_F(DestroyUserDataTest, DestroySdkData_WithDeFlag) {
+ android::os::CreateAppDataResult result;
+ android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+ args.packageName = "com.foo";
+ // Create the app user data.
+ ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+ // Destroy user data
+ ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_DE));
+ ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox"));
+ ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox"));
}
} // namespace installd
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index a7a8624..38c1c05 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -14,11 +14,14 @@
* 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>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "InstalldNativeService.h"
@@ -45,6 +48,8 @@
namespace android {
namespace installd {
+using ::testing::UnorderedElementsAre;
+
class UtilsTest : public testing::Test {
protected:
virtual void SetUp() {
@@ -656,38 +661,98 @@
ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700));
}
-TEST_F(UtilsTest, TestSupplementalDataPaths) {
+TEST_F(UtilsTest, TestForEachSubdir) {
+ auto deleter = [&]() {
+ delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */);
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ system("mkdir -p /data/local/tmp/user/0/com.foo");
+ system("mkdir -p /data/local/tmp/user/0/com.bar");
+ system("touch /data/local/tmp/user/0/some-file");
+
+ std::vector<std::string> result;
+ foreach_subdir("/data/local/tmp/user/0",
+ [&](const std::string &filename) { result.push_back(filename); });
+
+ EXPECT_THAT(result, UnorderedElementsAre("com.foo", "com.bar"));
+}
+
+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"));
+ EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/bar@random",
+ create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "bar", "random"));
// 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"));
+ EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/bar@random",
+ create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "bar",
+ "random"));
+}
+
+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..8cfd123 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,42 +198,55 @@
}
/**
- * 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/<package-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/<package-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());
}
+/**
+ * Create the path name where per-code level data for a particular app will be stored.
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>/<sdk-name>-<random-suffix>
+ */
+std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData,
+ userid_t user, const char* package_name,
+ const char* sdk_name, const char* randomSuffix) {
+ check_package_name(sdk_name);
+ auto package_path =
+ create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user, package_name);
+ return StringPrintf("%s/%s@%s", package_path.c_str(), sdk_name, randomSuffix);
+}
+
std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) {
return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
}
@@ -693,6 +709,34 @@
return std::unique_ptr<DIR, DirCloser>(::opendir(dir));
}
+// Collects filename of subdirectories of given directory and passes it to the function
+int foreach_subdir(const std::string& pathname, const std::function<void(const std::string&)> fn) {
+ auto dir = open_dir(pathname.c_str());
+ if (!dir) return -1;
+
+ int dfd = dirfd(dir.get());
+ if (dfd < 0) {
+ ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno));
+ return -1;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir.get()))) {
+ if (de->d_type != DT_DIR) {
+ continue;
+ }
+
+ std::string name{de->d_name};
+ // always skip "." and ".."
+ if (name == "." || name == "..") {
+ continue;
+ }
+ fn(name);
+ }
+
+ return 0;
+}
+
void cleanup_invalid_package_dirs_under_path(const std::string& pathname) {
auto dir = open_dir(pathname.c_str());
if (!dir) {
@@ -1096,30 +1140,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 +1391,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..54d77f9 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -32,6 +32,7 @@
#define MEASURE_DEBUG 0
#define FIXUP_DEBUG 0
+#define SDK_DEBUG 1
#define BYPASS_QUOTA 0
#define BYPASS_SDCARDFS 0
@@ -60,12 +61,15 @@
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_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData,
+ userid_t userid, const char* package_name,
+ const char* sdk_name, const char* randomSuffix);
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);
@@ -130,6 +134,8 @@
bool is_renamed_deleted_dir(const std::string& path);
int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = true);
+int foreach_subdir(const std::string& pathname, std::function<void(const std::string&)> fn);
+
void cleanup_invalid_package_dirs_under_path(const std::string& pathname);
int delete_dir_contents(const char *pathname,
@@ -160,7 +166,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 +184,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/Android.bp b/libs/binder/Android.bp
index 7448308..63d87da 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -166,6 +166,7 @@
"-Wextra-semi",
"-Werror",
"-Wzero-as-null-pointer-constant",
+ "-Wreorder-init-list",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 0970ca5..01b25d3 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -19,6 +19,7 @@
#include <atomic>
#include <set>
+#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
@@ -281,9 +282,11 @@
err = pingBinder();
break;
case EXTENSION_TRANSACTION:
+ CHECK(reply != nullptr);
err = reply->writeStrongBinder(getExtension());
break;
case DEBUG_PID_TRANSACTION:
+ CHECK(reply != nullptr);
err = reply->writeInt32(getDebugPid());
break;
case SET_RPC_CLIENT_TRANSACTION: {
@@ -590,6 +593,7 @@
{
switch (code) {
case INTERFACE_TRANSACTION:
+ CHECK(reply != nullptr);
reply->writeString16(getInterfaceDescriptor());
return NO_ERROR;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 056ef0a..921e57c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -72,29 +72,29 @@
e.cleanupCookie = cleanupCookie;
e.func = func;
- if (ssize_t idx = mObjects.indexOfKey(objectID); idx >= 0) {
+ if (mObjects.find(objectID) != mObjects.end()) {
ALOGI("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object "
"ID already in use",
objectID, this, object);
- return mObjects[idx].object;
+ return mObjects[objectID].object;
}
- mObjects.add(objectID, e);
+ mObjects.insert({objectID, e});
return nullptr;
}
void* BpBinder::ObjectManager::find(const void* objectID) const
{
- const ssize_t i = mObjects.indexOfKey(objectID);
- if (i < 0) return nullptr;
- return mObjects.valueAt(i).object;
+ auto i = mObjects.find(objectID);
+ if (i == mObjects.end()) return nullptr;
+ return i->second.object;
}
void* BpBinder::ObjectManager::detach(const void* objectID) {
- ssize_t idx = mObjects.indexOfKey(objectID);
- if (idx < 0) return nullptr;
- void* value = mObjects[idx].object;
- mObjects.removeItemsAt(idx, 1);
+ auto i = mObjects.find(objectID);
+ if (i == mObjects.end()) return nullptr;
+ void* value = i->second.object;
+ mObjects.erase(i);
return value;
}
@@ -102,10 +102,10 @@
{
const size_t N = mObjects.size();
ALOGV("Killing %zu objects in manager %p", N, this);
- for (size_t i=0; i<N; i++) {
- const entry_t& e = mObjects.valueAt(i);
+ for (auto i : mObjects) {
+ const entry_t& e = i.second;
if (e.func != nullptr) {
- e.func(mObjects.keyAt(i), e.object, e.cleanupCookie);
+ e.func(i.first, e.object, e.cleanupCookie);
}
}
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index bd974b0..9c7ff97 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -31,9 +31,10 @@
#include <binder/Parcel.h>
#include <log/log.h>
-#include <utils/KeyedVector.h>
#include <utils/threads.h>
+#include <map>
+
#define VERBOSE 0
namespace android {
@@ -63,7 +64,7 @@
void free_heap(const wp<IBinder>& binder);
Mutex mHeapCacheLock; // Protects entire vector below.
- KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
+ std::map<wp<IBinder>, heap_info_t> mHeapCache;
// We do not use the copy-on-write capabilities of KeyedVector.
// TODO: Reimplemement based on standard C++ container?
};
@@ -434,9 +435,9 @@
sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
{
Mutex::Autolock _l(mHeapCacheLock);
- ssize_t i = mHeapCache.indexOfKey(binder);
- if (i>=0) {
- heap_info_t& info = mHeapCache.editValueAt(i);
+ auto i = mHeapCache.find(binder);
+ if (i != mHeapCache.end()) {
+ heap_info_t& info = i->second;
ALOGD_IF(VERBOSE,
"found binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
binder.get(), info.heap.get(),
@@ -452,7 +453,7 @@
info.count = 1;
//ALOGD("adding binder=%p, heap=%p, count=%d",
// binder.get(), info.heap.get(), info.count);
- mHeapCache.add(binder, info);
+ mHeapCache.insert({binder, info});
return info.heap;
}
}
@@ -466,9 +467,9 @@
sp<IMemoryHeap> rel;
{
Mutex::Autolock _l(mHeapCacheLock);
- ssize_t i = mHeapCache.indexOfKey(binder);
- if (i>=0) {
- heap_info_t& info(mHeapCache.editValueAt(i));
+ auto i = mHeapCache.find(binder);
+ if (i != mHeapCache.end()) {
+ heap_info_t& info = i->second;
if (--info.count == 0) {
ALOGD_IF(VERBOSE,
"removing binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
@@ -477,8 +478,8 @@
static_cast<BpMemoryHeap*>(info.heap.get())
->mHeapId.load(memory_order_relaxed),
info.count);
- rel = mHeapCache.valueAt(i).heap;
- mHeapCache.removeItemsAt(i);
+ rel = i->second.heap;
+ mHeapCache.erase(i);
}
} else {
ALOGE("free_heap binder=%p not found!!!", binder.unsafe_get());
@@ -490,23 +491,23 @@
{
sp<IMemoryHeap> realHeap;
Mutex::Autolock _l(mHeapCacheLock);
- ssize_t i = mHeapCache.indexOfKey(binder);
- if (i>=0) realHeap = mHeapCache.valueAt(i).heap;
- else realHeap = interface_cast<IMemoryHeap>(binder);
+ auto i = mHeapCache.find(binder);
+ if (i != mHeapCache.end())
+ realHeap = i->second.heap;
+ else
+ realHeap = interface_cast<IMemoryHeap>(binder);
return realHeap;
}
void HeapCache::dump_heaps()
{
Mutex::Autolock _l(mHeapCacheLock);
- int c = mHeapCache.size();
- for (int i=0 ; i<c ; i++) {
- const heap_info_t& info = mHeapCache.valueAt(i);
+ for (const auto& i : mHeapCache) {
+ const heap_info_t& info = i.second;
BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(info.heap.get()));
- ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)",
- mHeapCache.keyAt(i).unsafe_get(),
- info.heap.get(), info.count,
- h->mHeapId.load(memory_order_relaxed), h->mBase, h->mSize);
+ ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)", i.first.unsafe_get(),
+ info.heap.get(), info.count, h->mHeapId.load(memory_order_relaxed), h->mBase,
+ h->mSize);
}
}
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 13f0a4c..f79075d 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1199,7 +1199,8 @@
case BR_DECREFS:
refs = (RefBase::weakref_type*)mIn.readPointer();
- obj = (BBinder*)mIn.readPointer();
+ // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
+ obj = (BBinder*)mIn.readPointer(); // consume
// NOTE: This assertion is not valid, because the object may no
// longer exist (thus the (BBinder*)cast above resulting in a different
// memory address).
@@ -1409,7 +1410,7 @@
uint32_t *async_received)
{
int ret = 0;
- binder_frozen_status_info info;
+ binder_frozen_status_info info = {};
info.pid = pid;
#if defined(__ANDROID__)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index f84f639..504c6c2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -63,7 +63,7 @@
// This macro should never be used at runtime, as a too large value
// of s could cause an integer overflow. Instead, you should always
// use the wrapper function pad_size()
-#define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
+#define PAD_SIZE_UNSAFE(s) (((s) + 3) & ~3UL)
static size_t pad_size(size_t s) {
if (s > (std::numeric_limits<size_t>::max() - 3)) {
@@ -200,7 +200,6 @@
}
flat_binder_object obj;
- obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
int schedBits = 0;
if (!IPCThreadState::self()->backgroundSchedulingDisabled()) {
@@ -221,6 +220,7 @@
const int32_t handle = proxy ? proxy->getPrivateAccessor().binderHandle() : 0;
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
+ obj.flags = 0;
obj.handle = handle;
obj.cookie = 0;
} else {
@@ -231,6 +231,7 @@
// override value, since it is set explicitly
schedBits = schedPolicyMask(policy, priority);
}
+ obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (local->isRequestingSid()) {
obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
}
@@ -243,6 +244,7 @@
}
} else {
obj.hdr.type = BINDER_TYPE_BINDER;
+ obj.flags = 0;
obj.binder = 0;
obj.cookie = 0;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1821729..b14a838 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;
}
@@ -403,6 +409,28 @@
return 0;
}
+#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
+bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
+ static const char* const names[] = {
+ [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] =
+ DRIVER_FEATURES_PATH "oneway_spam_detection",
+ };
+ int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC);
+ char on;
+ if (fd == -1) {
+ ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__,
+ names[static_cast<int>(feature)], strerror(errno));
+ return false;
+ }
+ if (read(fd, &on, sizeof(on)) == -1) {
+ ALOGE("%s: error reading to %s: %s", __func__,
+ names[static_cast<int>(feature)], strerror(errno));
+ return false;
+ }
+ close(fd);
+ return on == '1';
+}
+
status_t ProcessState::enableOnewaySpamDetection(bool enable) {
uint32_t enableDetection = enable ? 1 : 0;
if (ioctl(mDriverFD, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enableDetection) == -1) {
@@ -446,7 +474,9 @@
uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
if (result == -1) {
- ALOGV("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+ ALOGE_IF(ProcessState::isDriverFeatureEnabled(
+ ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION),
+ "Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
}
return fd;
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index b84395e..e79cb86 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -152,8 +152,13 @@
}
status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
- return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
- // std::move'd from fd becomes -1 (!ok())
+ // Why passing raw fd? When fd is passed as reference, Clang analyzer sees that the variable
+ // `fd` is a moved-from object. To work-around the issue, unwrap the raw fd from the outer `fd`,
+ // pass the raw fd by value to the lambda, and then finally wrap it in unique_fd inside the
+ // lambda.
+ return setupClient([&, raw = fd.release()](const std::vector<uint8_t>& sessionId,
+ bool incoming) -> status_t {
+ unique_fd fd(raw);
if (!fd.ok()) {
fd = request();
if (!fd.ok()) return BAD_VALUE;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 4ddbce7..2e7084e 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -125,8 +125,8 @@
auto&& [it, inserted] = mNodeForAddress.insert({RpcWireAddress::toRaw(address),
BinderNode{
.binder = binder,
- .timesSent = 1,
.sentRef = binder,
+ .timesSent = 1,
}});
if (inserted) {
*outAddress = it->first;
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index c0454b6..8deb2fe 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -17,10 +17,10 @@
#pragma once
#include <binder/IBinder.h>
-#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
#include <utils/threads.h>
+#include <map>
#include <unordered_map>
#include <variant>
@@ -110,7 +110,7 @@
IBinder::object_cleanup_func func;
};
- KeyedVector<const void*, entry_t> mObjects;
+ std::map<const void*, entry_t> mObjects;
};
class PrivateAccessor {
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index ea40db8..bb55831 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -56,8 +56,16 @@
static const int DUMP_FLAG_PROTO = 1 << 4;
/**
- * Retrieve an existing service, blocking for a few seconds
- * if it doesn't yet exist.
+ * Retrieve an existing service, blocking for a few seconds if it doesn't yet exist. This
+ * does polling. A more efficient way to make sure you unblock as soon as the service is
+ * available is to use waitForService or to use service notifications.
+ *
+ * Warning: when using this API, typically, you should call it in a loop. It's dangerous to
+ * assume that nullptr could mean that the service is not available. The service could just
+ * be starting. Generally, whether a service exists, this information should be declared
+ * externally (for instance, an Android feature might imply the existence of a service,
+ * a system property, or in the case of services in the VINTF manifest, it can be checked
+ * with isDeclared).
*/
virtual sp<IBinder> getService( const String16& name) const = 0;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index cf8d8e4..0deee73 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -91,6 +91,12 @@
*/
size_t getThreadPoolMaxThreadCount() const;
+ enum class DriverFeature {
+ ONEWAY_SPAM_DETECTION,
+ };
+ // Determine whether a feature is supported by the binder driver.
+ static bool isDriverFeatureEnabled(const DriverFeature feature);
+
private:
static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 2c471c6..7ea9be7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -268,7 +268,11 @@
const char* getMessage() const { return AStatus_getMessage(get()); }
std::string getDescription() const {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 30, *)) {
+#else
+ if (__ANDROID_API__ >= 30) {
+#endif
const char* cStr = AStatus_getDescription(get());
std::string ret = cStr;
AStatus_deleteDescription(cStr);
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 09411e7..b3bc7f4 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -256,7 +256,11 @@
// ourselves. The defaults are harmless.
AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
#ifdef HAS_BINDER_SHELL_COMMAND
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 30, *)) {
+#else
+ if (__ANDROID_API__ >= 30) {
+#endif
AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
}
#endif
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 972eca7..28819bb 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -51,7 +51,11 @@
AParcelableHolder(const AParcelableHolder& other)
: mParcel(AParcel_create()), mStability(other.mStability) {
// AParcelableHolder has been introduced in 31.
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
AParcel_getDataSize(other.mParcel.get()));
}
@@ -63,13 +67,21 @@
binder_status_t writeToParcel(AParcel* parcel) const {
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
int32_t size = AParcel_getDataSize(this->mParcel.get());
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
} else {
return STATUS_INVALID_OPERATION;
}
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
int32_t size = AParcel_getDataSize(this->mParcel.get());
RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
} else {
@@ -79,7 +91,11 @@
}
binder_status_t readFromParcel(const AParcel* parcel) {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
AParcel_reset(mParcel.get());
} else {
return STATUS_INVALID_OPERATION;
@@ -99,7 +115,11 @@
return STATUS_BAD_VALUE;
}
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
} else {
status = STATUS_INVALID_OPERATION;
@@ -115,7 +135,11 @@
if (this->mStability > T::_aidl_stability) {
return STATUS_BAD_VALUE;
}
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
AParcel_reset(mParcel.get());
} else {
return STATUS_INVALID_OPERATION;
@@ -129,7 +153,11 @@
binder_status_t getParcelable(std::optional<T>* ret) const {
const std::string parcelableDesc(T::descriptor);
AParcel_setDataPosition(mParcel.get(), 0);
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
if (AParcel_getDataSize(mParcel.get()) == 0) {
*ret = std::nullopt;
return STATUS_OK;
@@ -153,7 +181,11 @@
}
void reset() {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 31, *)) {
+#else
+ if (__ANDROID_API__ >= 31) {
+#endif
AParcel_reset(mParcel.get());
}
}
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 2a66941..dfa8ea2 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -53,11 +53,19 @@
/**
* Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
* it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
- * for calling AIBinder_decStrong).
+ * for calling AIBinder_decStrong). This does polling. A more efficient way to make sure you
+ * unblock as soon as the service is available is to use AIBinder_waitForService.
*
* WARNING: when using this API across an APEX boundary, do not use with unstable
* AIDL services. TODO(b/139325195)
*
+ * WARNING: when using this API, typically, you should call it in a loop. It's dangerous to
+ * assume that nullptr could mean that the service is not available. The service could just
+ * be starting. Generally, whether a service exists, this information should be declared
+ * externally (for instance, an Android feature might imply the existence of a service,
+ * a system property, or in the case of services in the VINTF manifest, it can be checked
+ * with AServiceManager_isDeclared).
+ *
* \param instance identifier of the service used to lookup the service.
*/
__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 7c5afde..c9d6af0 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -924,9 +924,9 @@
BinderFeatures::default(),
);
- assert!(!(service1 < service1));
- assert!(!(service1 > service1));
- assert_eq!(service1 < service2, !(service2 < service1));
+ assert!((service1 >= service1));
+ assert!((service1 <= service1));
+ assert_eq!(service1 < service2, (service2 >= service1));
}
#[test]
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 700940a..b1e17b7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -57,6 +57,7 @@
using android::base::testing::HasValue;
using android::base::testing::Ok;
using testing::ExplainMatchResult;
+using testing::Matcher;
using testing::Not;
using testing::WithParamInterface;
@@ -1258,12 +1259,23 @@
class BinderLibRpcTest : public BinderLibRpcTestBase {};
+// e.g. EXPECT_THAT(expr, Debuggable(StatusEq(...))
+// If device is debuggable AND not on user builds, expects matcher.
+// Otherwise expects INVALID_OPERATION.
+// Debuggable + non user builds is necessary but not sufficient for setRpcClientDebug to work.
+static Matcher<status_t> Debuggable(const Matcher<status_t> &matcher) {
+ bool isDebuggable = android::base::GetBoolProperty("ro.debuggable", false) &&
+ android::base::GetProperty("ro.build.type", "") != "user";
+ return isDebuggable ? matcher : StatusEq(INVALID_OPERATION);
+}
+
TEST_F(BinderLibRpcTest, SetRpcClientDebug) {
auto binder = addServer();
ASSERT_TRUE(binder != nullptr);
auto [socket, port] = CreateSocket();
ASSERT_TRUE(socket.ok());
- EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), sp<BBinder>::make()), StatusEq(OK));
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), sp<BBinder>::make()),
+ Debuggable(StatusEq(OK)));
}
// Tests for multiple RpcServer's on the same binder object.
@@ -1274,12 +1286,14 @@
auto [socket1, port1] = CreateSocket();
ASSERT_TRUE(socket1.ok());
auto keepAliveBinder1 = sp<BBinder>::make();
- EXPECT_THAT(binder->setRpcClientDebug(std::move(socket1), keepAliveBinder1), StatusEq(OK));
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket1), keepAliveBinder1),
+ Debuggable(StatusEq(OK)));
auto [socket2, port2] = CreateSocket();
ASSERT_TRUE(socket2.ok());
auto keepAliveBinder2 = sp<BBinder>::make();
- EXPECT_THAT(binder->setRpcClientDebug(std::move(socket2), keepAliveBinder2), StatusEq(OK));
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket2), keepAliveBinder2),
+ Debuggable(StatusEq(OK)));
}
// Negative tests for RPC APIs on IBinder. Call should fail in the same way on both remote and
@@ -1298,7 +1312,7 @@
auto binder = GetService();
ASSERT_TRUE(binder != nullptr);
EXPECT_THAT(binder->setRpcClientDebug(android::base::unique_fd(), sp<BBinder>::make()),
- StatusEq(BAD_VALUE));
+ Debuggable(StatusEq(BAD_VALUE)));
}
TEST_P(BinderLibRpcTestP, SetRpcClientDebugNoKeepAliveBinder) {
@@ -1306,7 +1320,8 @@
ASSERT_TRUE(binder != nullptr);
auto [socket, port] = CreateSocket();
ASSERT_TRUE(socket.ok());
- EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), nullptr), StatusEq(UNEXPECTED_NULL));
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), nullptr),
+ Debuggable(StatusEq(UNEXPECTED_NULL)));
}
INSTANTIATE_TEST_CASE_P(BinderLibTest, BinderLibRpcTestP, testing::Bool(),
BinderLibRpcTestP::ParamToString);
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 1513eca..45a6d47 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -31,6 +31,7 @@
#include <android-base/unique_fd.h>
#include <bpf/BpfMap.h>
#include <cputimeinstate.h>
+#include <cutils/android_filesystem_config.h>
#include <libbpf.h>
namespace android {
@@ -219,6 +220,7 @@
uint32_t totalFreqsCount = totalTimes.size();
std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
for (auto const &[uid, uidTimes]: *allUid) {
+ if (uid == AID_SDK_SANDBOX) continue;
for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
}
@@ -646,5 +648,55 @@
}
}
+void *forceSwitchWithUid(void *uidPtr) {
+ if (!uidPtr) return nullptr;
+ setuid(*(uint32_t *)uidPtr);
+
+ // Sleep briefly to trigger a context switch, ensuring we see at least one update.
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ return nullptr;
+}
+
+TEST_F(TimeInStateTest, SdkSandboxUid) {
+ // Find an unused app UID and its corresponding SDK sandbox uid.
+ uint32_t appUid = AID_APP_START, sandboxUid;
+ {
+ auto times = getUidsCpuFreqTimes();
+ ASSERT_TRUE(times.has_value());
+ ASSERT_FALSE(times->empty());
+ for (const auto &kv : *times) {
+ if (kv.first > AID_APP_END) break;
+ appUid = std::max(appUid, kv.first);
+ }
+ appUid++;
+ sandboxUid = appUid + (AID_SDK_SANDBOX_PROCESS_START - AID_APP_START);
+ }
+
+ // Create a thread to run with the fake sandbox uid.
+ pthread_t thread;
+ ASSERT_EQ(pthread_create(&thread, NULL, &forceSwitchWithUid, &sandboxUid), 0);
+ pthread_join(thread, NULL);
+
+ // Confirm we recorded stats for appUid and AID_SDK_SANDBOX but not sandboxUid
+ auto allTimes = getUidsCpuFreqTimes();
+ ASSERT_TRUE(allTimes.has_value());
+ ASSERT_FALSE(allTimes->empty());
+ ASSERT_NE(allTimes->find(appUid), allTimes->end());
+ ASSERT_NE(allTimes->find(AID_SDK_SANDBOX), allTimes->end());
+ ASSERT_EQ(allTimes->find(sandboxUid), allTimes->end());
+
+ auto allConcurrentTimes = getUidsConcurrentTimes();
+ ASSERT_TRUE(allConcurrentTimes.has_value());
+ ASSERT_FALSE(allConcurrentTimes->empty());
+ ASSERT_NE(allConcurrentTimes->find(appUid), allConcurrentTimes->end());
+ ASSERT_NE(allConcurrentTimes->find(AID_SDK_SANDBOX), allConcurrentTimes->end());
+ ASSERT_EQ(allConcurrentTimes->find(sandboxUid), allConcurrentTimes->end());
+
+ ASSERT_TRUE(clearUidTimes(appUid));
+}
+
} // namespace bpf
} // namespace android
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 7ce72ff..ec4c7c1 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,12 +132,14 @@
}
}
-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) {
+ mTransactionReadyCallback(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
@@ -180,11 +182,12 @@
static_cast<uint32_t>(mPendingTransactions.size()));
SurfaceComposerClient::Transaction t;
mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
- t.setApplyToken(mApplyToken).apply();
+ // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+ t.setApplyToken(mApplyToken).apply(false, true);
}
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 +196,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 +205,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,16 +224,15 @@
// 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) {
- t.setApplyToken(mApplyToken).apply();
+ // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+ t.setApplyToken(mApplyToken).apply(false, true);
}
}
@@ -498,7 +500,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 +518,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 +540,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());
@@ -549,7 +553,13 @@
mergePendingTransactions(t, bufferItem.mFrameNumber);
if (applyTransaction) {
- t->setApplyToken(mApplyToken).apply();
+ // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+ t->setApplyToken(mApplyToken).apply(false, true);
+ mAppliedLastTransaction = true;
+ mLastAppliedFrameNumber = bufferItem.mFrameNumber;
+ } else {
+ t->setBufferHasBarrier(mSurfaceControl, mLastAppliedFrameNumber);
+ mAppliedLastTransaction = false;
}
BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
@@ -607,60 +617,69 @@
}
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
- BBQ_TRACE();
- std::unique_lock _lock{mMutex};
+ std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
+ SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+ {
+ BBQ_TRACE();
+ std::unique_lock _lock{mMutex};
+ const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
+ BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
- const bool syncTransactionSet = mSyncTransaction != nullptr;
- BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
+ if (syncTransactionSet) {
+ bool mayNeedToWaitForBuffer = true;
+ // If we are going to re-use the same mSyncTransaction, release the buffer that may
+ // already be set in the Transaction. This is to allow us a free slot early to continue
+ // processing a new buffer.
+ if (!mAcquireSingleBuffer) {
+ auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
+ if (bufferData) {
+ BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
+ bufferData->frameNumber);
+ releaseBuffer(bufferData->generateReleaseCallbackId(),
+ bufferData->acquireFence);
+ // Because we just released a buffer, we know there's no need to wait for a free
+ // buffer.
+ mayNeedToWaitForBuffer = false;
+ }
+ }
- if (syncTransactionSet) {
- bool mayNeedToWaitForBuffer = true;
- // If we are going to re-use the same mSyncTransaction, release the buffer that may already
- // be set in the Transaction. This is to allow us a free slot early to continue processing
- // a new buffer.
- if (!mAcquireSingleBuffer) {
- auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
- if (bufferData) {
- BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
- bufferData->frameNumber);
- releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence);
- // Because we just released a buffer, we know there's no need to wait for a free
- // buffer.
- mayNeedToWaitForBuffer = false;
+ if (mayNeedToWaitForBuffer) {
+ flushAndWaitForFreeBuffer(_lock);
}
}
- if (mayNeedToWaitForBuffer) {
- flushAndWaitForFreeBuffer(_lock);
+ // add to shadow queue
+ mNumFrameAvailable++;
+ if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
+ acquireAndReleaseBuffer();
+ }
+ ATRACE_INT(mQueuedBufferTrace.c_str(),
+ mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
+
+ BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s",
+ item.mFrameNumber, boolToString(syncTransactionSet));
+
+ if (syncTransactionSet) {
+ acquireNextBufferLocked(mSyncTransaction);
+
+ // Only need a commit callback when syncing to ensure the buffer that's synced has been
+ // sent to SF
+ incStrong((void*)transactionCommittedCallbackThunk);
+ mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
+ static_cast<void*>(this));
+ mWaitForTransactionCallback = true;
+ if (mAcquireSingleBuffer) {
+ prevCallback = mTransactionReadyCallback;
+ prevTransaction = mSyncTransaction;
+ mTransactionReadyCallback = nullptr;
+ mSyncTransaction = nullptr;
+ }
+ } else if (!mWaitForTransactionCallback) {
+ acquireNextBufferLocked(std::nullopt);
}
}
-
- // add to shadow queue
- mNumFrameAvailable++;
- if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
- acquireAndReleaseBuffer();
- }
- ATRACE_INT(mQueuedBufferTrace.c_str(),
- mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
-
- BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber,
- boolToString(syncTransactionSet));
-
- if (syncTransactionSet) {
- acquireNextBufferLocked(mSyncTransaction);
-
- // Only need a commit callback when syncing to ensure the buffer that's synced has been sent
- // to SF
- incStrong((void*)transactionCommittedCallbackThunk);
- mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
- static_cast<void*>(this));
-
- if (mAcquireSingleBuffer) {
- mSyncTransaction = nullptr;
- }
- mWaitForTransactionCallback = true;
- } else if (!mWaitForTransactionCallback) {
- acquireNextBufferLocked(std::nullopt);
+ if (prevCallback) {
+ prevCallback(prevTransaction);
}
}
@@ -679,12 +698,37 @@
mDequeueTimestamps.erase(bufferId);
};
-void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t,
- bool acquireSingleBuffer) {
+void BLASTBufferQueue::syncNextTransaction(
+ std::function<void(SurfaceComposerClient::Transaction*)> callback,
+ bool acquireSingleBuffer) {
BBQ_TRACE();
std::lock_guard _lock{mMutex};
- mSyncTransaction = t;
- mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true;
+ mTransactionReadyCallback = callback;
+ if (callback) {
+ mSyncTransaction = new SurfaceComposerClient::Transaction();
+ } else {
+ mSyncTransaction = nullptr;
+ }
+ mAcquireSingleBuffer = mTransactionReadyCallback ? acquireSingleBuffer : true;
+}
+
+void BLASTBufferQueue::stopContinuousSyncTransaction() {
+ std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
+ SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+ {
+ std::lock_guard _lock{mMutex};
+ bool invokeCallback = mTransactionReadyCallback && !mAcquireSingleBuffer;
+ if (invokeCallback) {
+ prevCallback = mTransactionReadyCallback;
+ prevTransaction = mSyncTransaction;
+ }
+ mTransactionReadyCallback = nullptr;
+ mSyncTransaction = nullptr;
+ mAcquireSingleBuffer = true;
+ }
+ if (prevCallback) {
+ prevCallback(prevTransaction);
+ }
}
bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
@@ -821,7 +865,8 @@
SurfaceComposerClient::Transaction t;
mergePendingTransactions(&t, frameNumber);
- t.setApplyToken(mApplyToken).apply();
+ // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+ t.setApplyToken(mApplyToken).apply(false, true);
}
void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
@@ -1014,7 +1059,8 @@
static_cast<uint32_t>(mPendingTransactions.size()));
SurfaceComposerClient::Transaction t;
mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
- t.setApplyToken(mApplyToken).apply();
+ // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+ t.setApplyToken(mApplyToken).apply(false, true);
}
// Clear sync states
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 75c5e26..5532c6e 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>
@@ -109,7 +111,13 @@
SAFE_PARCEL(data.writeUint64, transactionId);
- return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
+ if (flags & ISurfaceComposer::eOneWay) {
+ return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
+ data, &reply, IBinder::FLAG_ONEWAY);
+ } else {
+ return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
+ data, &reply);
+ }
}
void bootFinished() override {
@@ -118,36 +126,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 +1429,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..338ff11 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};
}
@@ -792,6 +796,8 @@
SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote());
SAFE_PARCEL(output->writeUint64, cachedBuffer.id);
+ SAFE_PARCEL(output->writeBool, hasBarrier);
+ SAFE_PARCEL(output->writeUint64, barrierFrameNumber);
return NO_ERROR;
}
@@ -828,6 +834,9 @@
cachedBuffer.token = tmpBinder;
SAFE_PARCEL(input->readUint64, &cachedBuffer.id);
+ SAFE_PARCEL(input->readBool, &hasBarrier);
+ SAFE_PARCEL(input->readUint64, &barrierFrameNumber);
+
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9269c3e..27856ce 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;
@@ -882,7 +930,7 @@
}
}
-status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
+status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
if (mStatus != NO_ERROR) {
return mStatus;
}
@@ -936,6 +984,14 @@
if (mAnimation) {
flags |= ISurfaceComposer::eAnimation;
}
+ if (oneWay) {
+ if (mForceSynchronous) {
+ ALOGE("Transaction attempted to set synchronous and one way at the same time"
+ " this is an invalid request. Synchronous will win for safety");
+ } else {
+ flags |= ISurfaceComposer::eOneWay;
+ }
+ }
// If both mEarlyWakeupStart and mEarlyWakeupEnd are set
// it is equivalent for none
@@ -1351,6 +1407,18 @@
return bufferData;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferHasBarrier(
+ const sp<SurfaceControl>& sc, uint64_t barrierFrameNumber) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->bufferData->hasBarrier = true;
+ s->bufferData->barrierFrameNumber = barrierFrameNumber;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
@@ -2267,26 +2335,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/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 80bd638..2312a8c 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -51,11 +51,11 @@
}
bool WindowInfo::isSpy() const {
- return inputFeatures.test(Feature::SPY);
+ return inputConfig.test(InputConfig::SPY);
}
bool WindowInfo::interceptsStylus() const {
- return inputFeatures.test(Feature::INTERCEPTS_STYLUS);
+ return inputConfig.test(InputConfig::INTERCEPTS_STYLUS);
}
bool WindowInfo::overlaps(const WindowInfo* other) const {
@@ -73,8 +73,7 @@
info.touchableRegion.hasSameRects(touchableRegion) &&
info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid &&
info.ownerUid == ownerUid && info.packageName == packageName &&
- info.inputFeatures == inputFeatures && info.inputConfig == inputConfig &&
- info.displayId == displayId &&
+ info.inputConfig == inputConfig && info.displayId == displayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType &&
info.layoutParamsFlags == layoutParamsFlags;
@@ -92,7 +91,6 @@
parcel->writeInt32(1);
// Ensure that the size of the flags that we use is 32 bits for writing into the parcel.
- static_assert(sizeof(inputFeatures) == 4u);
static_assert(sizeof(inputConfig) == 4u);
// clang-format off
@@ -120,7 +118,6 @@
parcel->writeInt32(ownerPid) ?:
parcel->writeInt32(ownerUid) ?:
parcel->writeUtf8AsUtf16(packageName) ?:
- parcel->writeInt32(inputFeatures.get()) ?:
parcel->writeInt32(inputConfig.get()) ?:
parcel->writeInt32(displayId) ?:
applicationInfo.writeToParcel(parcel) ?:
@@ -148,12 +145,14 @@
return status;
}
- layoutParamsFlags = Flags<Flag>(parcel->readInt32());
- layoutParamsType = static_cast<Type>(parcel->readInt32());
float dsdx, dtdx, tx, dtdy, dsdy, ty;
- int32_t touchOcclusionModeInt;
+ int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt;
+ sp<IBinder> touchableRegionCropHandleSp;
+
// clang-format off
- status = parcel->readInt32(&frameLeft) ?:
+ status = parcel->readInt32(&lpFlags) ?:
+ parcel->readInt32(&lpType) ?:
+ parcel->readInt32(&frameLeft) ?:
parcel->readInt32(&frameTop) ?:
parcel->readInt32(&frameRight) ?:
parcel->readInt32(&frameBottom) ?:
@@ -169,33 +168,28 @@
parcel->readInt32(&touchOcclusionModeInt) ?:
parcel->readInt32(&ownerPid) ?:
parcel->readInt32(&ownerUid) ?:
- parcel->readUtf8FromUtf16(&packageName);
- // clang-format on
-
- if (status != OK) {
- return status;
- }
-
- touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
-
- inputFeatures = Flags<Feature>(parcel->readInt32());
- inputConfig = Flags<InputConfig>(parcel->readInt32());
- // clang-format off
- status = parcel->readInt32(&displayId) ?:
+ parcel->readUtf8FromUtf16(&packageName) ?:
+ parcel->readInt32(&inputConfigInt) ?:
+ parcel->readInt32(&displayId) ?:
applicationInfo.readFromParcel(parcel) ?:
parcel->read(touchableRegion) ?:
- parcel->readBool(&replaceTouchableRegionWithCrop);
+ parcel->readBool(&replaceTouchableRegionWithCrop) ?:
+ parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
+ parcel->readNullableStrongBinder(&windowToken);
// clang-format on
if (status != OK) {
return status;
}
- touchableRegionCropHandle = parcel->readStrongBinder();
+ layoutParamsFlags = Flags<Flag>(lpFlags);
+ layoutParamsType = static_cast<Type>(lpType);
transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+ touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
+ inputConfig = Flags<InputConfig>(inputConfigInt);
+ touchableRegionCropHandle = touchableRegionCropHandleSp;
- status = parcel->readNullableStrongBinder(&windowToken);
- return status;
+ return OK;
}
// --- WindowInfoHandle ---
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..65fc04d 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);
@@ -95,13 +95,14 @@
const std::vector<SurfaceControlStats>& stats);
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount);
- void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true);
+ void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
+ bool acquireSingleBuffer = true);
+ void stopContinuousSyncTransaction();
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
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);
@@ -214,15 +215,12 @@
sp<IGraphicBufferProducer> mProducer;
sp<BLASTBufferItemConsumer> mBufferItemConsumer;
+ std::function<void(SurfaceComposerClient::Transaction*)> mTransactionReadyCallback
+ GUARDED_BY(mMutex);
SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
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 +248,27 @@
// 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;
+
+ // We send all transactions on our apply token over one-way binder calls to avoid blocking
+ // client threads. All of our transactions remain in order, since they are one-way binder calls
+ // from a single process, to a single interface. However once we give up a Transaction for sync
+ // we can start to have ordering issues. When we return from sync to normal frame production,
+ // we wait on the commit callback of sync frames ensuring ordering, however we don't want to
+ // wait on the commit callback for every normal frame (since even emitting them has a
+ // performance cost) this means we need a method to ensure frames are in order when switching
+ // from one-way application on our apply token, to application on some other apply token. We
+ // make use of setBufferHasBarrier to declare this ordering. This boolean simply tracks when we
+ // need to set this flag, notably only in the case where we are transitioning from a previous
+ // transaction applied by us (one way, may not yet have reached server) and an upcoming
+ // transaction that will be applied by some sync consumer.
+ bool mAppliedLastTransaction = false;
+ uint64_t mLastAppliedFrameNumber = 0;
};
} // 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..0a3cc19 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;
@@ -108,6 +113,7 @@
// android.permission.ACCESS_SURFACE_FLINGER
eEarlyWakeupStart = 0x08,
eEarlyWakeupEnd = 0x10,
+ eOneWay = 0x20
};
enum VsyncSource {
@@ -261,27 +267,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 +606,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 +634,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..0f37dab 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>
@@ -87,6 +89,8 @@
// Used by BlastBufferQueue to forward the framenumber generated by the
// graphics producer.
uint64_t frameNumber = 0;
+ bool hasBarrier = false;
+ uint64_t barrierFrameNumber = 0;
// Listens to when the buffer is safe to be released. This is used for blast
// layers only. The callback includes a release fence as well as the graphic
@@ -128,8 +132,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 +379,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..c8ac166 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,
@@ -459,7 +461,7 @@
// Clears the contents of the transaction without applying it.
void clear();
- status_t apply(bool synchronous = false);
+ status_t apply(bool synchronous = false, bool oneWay = false);
// Merge another transaction in to this one, clearing other
// as if it had been applied.
Transaction& merge(Transaction&& other);
@@ -519,6 +521,27 @@
const std::optional<uint64_t>& frameNumber = std::nullopt,
ReleaseBufferCallback callback = nullptr);
std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
+
+ /**
+ * If this transaction, has a a buffer set for the given SurfaceControl
+ * mark that buffer as ordered after a given barrierFrameNumber.
+ *
+ * SurfaceFlinger will refuse to apply this transaction until after
+ * the frame in barrierFrameNumber has been applied. This transaction may
+ * be applied in the same frame as the barrier buffer or after.
+ *
+ * This is only designed to be used to handle switches between multiple
+ * apply tokens, as explained in the comment for BLASTBufferQueue::mAppliedLastTransaction.
+ *
+ * Has to be called after setBuffer.
+ *
+ * WARNING:
+ * This API is very dangerous to the caller, as if you invoke it without
+ * a frameNumber you have not yet submitted, you can dead-lock your
+ * SurfaceControl's transaction queue.
+ */
+ Transaction& setBufferHasBarrier(const sp<SurfaceControl>& sc,
+ uint64_t barrierFrameNumber);
Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index b9bffaa..ef0b98b 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -17,7 +17,7 @@
#pragma once
#include <android/gui/TouchOcclusionMode.h>
-#include <android/os/IInputConstants.h>
+#include <android/os/InputConfig.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <ftl/Flags.h>
@@ -132,49 +132,45 @@
ftl_last = FIRST_SYSTEM_WINDOW + 15
};
- // This is a conversion of os::IInputConstants::InputFeature to an enum backed by an unsigned
- // type. This indicates that they are flags, so it can be used with ftl/enum.h.
- enum class Feature : uint32_t {
- // clang-format off
- NO_INPUT_CHANNEL =
- static_cast<uint32_t>(os::IInputConstants::InputFeature::NO_INPUT_CHANNEL),
- DISABLE_USER_ACTIVITY =
- static_cast<uint32_t>(os::IInputConstants::InputFeature::DISABLE_USER_ACTIVITY),
- DROP_INPUT =
- static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT),
- DROP_INPUT_IF_OBSCURED =
- static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT_IF_OBSCURED),
- SPY =
- static_cast<uint32_t>(os::IInputConstants::InputFeature::SPY),
- INTERCEPTS_STYLUS =
- static_cast<uint32_t>(os::IInputConstants::InputFeature::INTERCEPTS_STYLUS),
- // clang-format on
- };
-
// Flags used to determine configuration of this input window.
- // Input windows can be configured with two sets of flags: InputFeature (WindowInfo::Feature
- // defined above), and InputConfig. When adding a new configuration for an input window:
- // - If you are adding a new flag that's visible and accessible to apps, it should be added
- // as an InputFeature.
- // - If you are adding an internal behaviour that is used within the system or shell and is
- // not exposed to apps, it should be added as an InputConfig.
+ // This is a conversion of os::InputConfig to an enum backed by an unsigned
+ // type. This indicates that they are flags, so it can be used with ftl/enum.h.
enum class InputConfig : uint32_t {
// clang-format off
- NONE = 0,
- NOT_VISIBLE = 1 << 0,
- NOT_FOCUSABLE = 1 << 1,
- NOT_TOUCHABLE = 1 << 2,
- PREVENT_SPLITTING = 1 << 3,
- DUPLICATE_TOUCH_TO_WALLPAPER = 1 << 4,
- IS_WALLPAPER = 1 << 5,
- PAUSE_DISPATCHING = 1 << 6,
- // This flag is set when the window is of a trusted type that is allowed to silently
- // overlay other windows for the purpose of implementing the secure views feature.
- // Trusted overlays, such as IME windows, can partly obscure other windows without causing
- // motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
- TRUSTED_OVERLAY = 1 << 7,
- WATCH_OUTSIDE_TOUCH = 1 << 8,
- SLIPPERY = 1 << 9,
+ DEFAULT =
+ static_cast<uint32_t>(os::InputConfig::DEFAULT),
+ NO_INPUT_CHANNEL =
+ static_cast<uint32_t>(os::InputConfig::NO_INPUT_CHANNEL),
+ NOT_VISIBLE =
+ static_cast<uint32_t>(os::InputConfig::NOT_VISIBLE),
+ NOT_FOCUSABLE =
+ static_cast<uint32_t>(os::InputConfig::NOT_FOCUSABLE),
+ NOT_TOUCHABLE =
+ static_cast<uint32_t>(os::InputConfig::NOT_TOUCHABLE),
+ PREVENT_SPLITTING =
+ static_cast<uint32_t>(os::InputConfig::PREVENT_SPLITTING),
+ DUPLICATE_TOUCH_TO_WALLPAPER =
+ static_cast<uint32_t>(os::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER),
+ IS_WALLPAPER =
+ static_cast<uint32_t>(os::InputConfig::IS_WALLPAPER),
+ PAUSE_DISPATCHING =
+ static_cast<uint32_t>(os::InputConfig::PAUSE_DISPATCHING),
+ TRUSTED_OVERLAY =
+ static_cast<uint32_t>(os::InputConfig::TRUSTED_OVERLAY),
+ WATCH_OUTSIDE_TOUCH =
+ static_cast<uint32_t>(os::InputConfig::WATCH_OUTSIDE_TOUCH),
+ SLIPPERY =
+ static_cast<uint32_t>(os::InputConfig::SLIPPERY),
+ DISABLE_USER_ACTIVITY =
+ static_cast<uint32_t>(os::InputConfig::DISABLE_USER_ACTIVITY),
+ DROP_INPUT =
+ static_cast<uint32_t>(os::InputConfig::DROP_INPUT),
+ DROP_INPUT_IF_OBSCURED =
+ static_cast<uint32_t>(os::InputConfig::DROP_INPUT_IF_OBSCURED),
+ SPY =
+ static_cast<uint32_t>(os::InputConfig::SPY),
+ INTERCEPTS_STYLUS =
+ static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS),
// clang-format on
};
@@ -228,7 +224,6 @@
int32_t ownerPid = -1;
int32_t ownerUid = -1;
std::string packageName;
- Flags<Feature> inputFeatures;
Flags<InputConfig> inputConfig;
int32_t displayId = ADISPLAY_ID_NONE;
InputApplicationInfo applicationInfo;
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..0c3236c 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>
@@ -109,15 +110,27 @@
mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888);
}
- void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) {
- mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer);
+ void setSyncTransaction(Transaction& next, bool acquireSingleBuffer = true) {
+ auto callback = [&next](Transaction* t) { next.merge(std::move(*t)); };
+ mBlastBufferQueueAdapter->syncNextTransaction(callback, acquireSingleBuffer);
+ }
+
+ void syncNextTransaction(std::function<void(Transaction*)> callback,
+ bool acquireSingleBuffer = true) {
+ mBlastBufferQueueAdapter->syncNextTransaction(callback, acquireSingleBuffer);
+ }
+
+ void stopContinuousSyncTransaction() {
+ mBlastBufferQueueAdapter->stopContinuousSyncTransaction();
}
int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
- Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; }
+ std::function<void(Transaction*)> getTransactionReadyCallback() {
+ return mBlastBufferQueueAdapter->mTransactionReadyCallback;
+ }
sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
@@ -283,13 +296,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;
@@ -342,7 +355,7 @@
ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
ASSERT_EQ(mDisplayWidth, adapter.getWidth());
ASSERT_EQ(mDisplayHeight, adapter.getHeight());
- ASSERT_EQ(nullptr, adapter.getSyncTransaction());
+ ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback());
}
TEST_F(BLASTBufferQueueTest, Update) {
@@ -363,11 +376,12 @@
ASSERT_EQ(mDisplayHeight / 2, height);
}
-TEST_F(BLASTBufferQueueTest, SetSyncTransaction) {
+TEST_F(BLASTBufferQueueTest, SyncNextTransaction) {
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- Transaction sync;
- adapter.setSyncTransaction(&sync);
- ASSERT_EQ(&sync, adapter.getSyncTransaction());
+ ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback());
+ auto callback = [](Transaction*) {};
+ adapter.syncNextTransaction(callback);
+ ASSERT_NE(nullptr, adapter.getTransactionReadyCallback());
}
TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
@@ -807,7 +821,7 @@
setUpProducer(adapter, igbProducer);
Transaction sync;
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
// queue non sync buffer, so this one should get blocked
@@ -847,12 +861,12 @@
Transaction mainTransaction;
Transaction sync;
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
mainTransaction.merge(std::move(sync));
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, r, g, b, 0);
mainTransaction.merge(std::move(sync));
@@ -888,7 +902,7 @@
Transaction sync;
// queue a sync transaction
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
mainTransaction.merge(std::move(sync));
@@ -897,7 +911,7 @@
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 1 buffer to be released because the non sync transaction should merge
// with the sync
@@ -936,7 +950,7 @@
Transaction sync;
// queue a sync transaction
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
mainTransaction.merge(std::move(sync));
@@ -947,7 +961,7 @@
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 3 buffers to be released because the non sync transactions should merge
// with the sync
@@ -993,7 +1007,7 @@
Transaction sync;
// queue a sync transaction
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
mainTransaction.merge(std::move(sync));
@@ -1007,7 +1021,7 @@
mainTransaction.apply();
// queue another sync transaction
- adapter.setSyncTransaction(&sync);
+ adapter.setSyncTransaction(sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 2 buffers to be released because the non sync transactions should merge
// with the sync
@@ -1030,19 +1044,19 @@
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
-TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) {
+TEST_F(BLASTBufferQueueTest, SyncNextTransactionAcquireMultipleBuffers) {
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
Transaction next;
- adapter.setSyncTransaction(&next, false);
+ adapter.setSyncTransaction(next, false);
queueBuffer(igbProducer, 0, 255, 0, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
// There should only be one frame submitted since the first frame will be released.
adapter.validateNumFramesSubmitted(1);
- adapter.setSyncTransaction(nullptr);
+ adapter.stopContinuousSyncTransaction();
// queue non sync buffer, so this one should get blocked
// Add a present delay to allow the first screenshot to get taken.
@@ -1096,7 +1110,7 @@
Transaction next;
queueBuffer(igbProducer, 0, 255, 0, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
- adapter.setSyncTransaction(&next, false);
+ adapter.setSyncTransaction(next, true);
queueBuffer(igbProducer, 255, 0, 0, 0);
CallbackHelper transactionCallback;
@@ -1139,7 +1153,7 @@
Transaction next;
queueBuffer(igbProducer, 0, 255, 0, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
- adapter.setSyncTransaction(&next, false);
+ adapter.setSyncTransaction(next, true);
queueBuffer(igbProducer, 255, 0, 0, 0);
CallbackHelper transactionCallback;
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/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ff9bae2..c51b244 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -64,7 +64,6 @@
i.ownerPid = 19;
i.ownerUid = 24;
i.packageName = "com.example.package";
- i.inputFeatures = WindowInfo::Feature::DISABLE_USER_ACTIVITY;
i.inputConfig = WindowInfo::InputConfig::NOT_FOCUSABLE;
i.displayId = 34;
i.replaceTouchableRegionWithCrop = true;
@@ -97,7 +96,6 @@
ASSERT_EQ(i.ownerPid, i2.ownerPid);
ASSERT_EQ(i.ownerUid, i2.ownerUid);
ASSERT_EQ(i.packageName, i2.packageName);
- ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
ASSERT_EQ(i.inputConfig, i2.inputConfig);
ASSERT_EQ(i.displayId, i2.displayId);
ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 930d819..606fe2a 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -30,6 +30,7 @@
"android/os/IInputConstants.aidl",
"android/os/InputEventInjectionResult.aidl",
"android/os/InputEventInjectionSync.aidl",
+ "android/os/InputConfig.aidl",
],
}
@@ -79,11 +80,8 @@
android: {
srcs: [
"InputTransport.cpp",
- "android/os/BlockUntrustedTouchesMode.aidl",
- "android/os/IInputConstants.aidl",
"android/os/IInputFlinger.aidl",
- "android/os/InputEventInjectionResult.aidl",
- "android/os/InputEventInjectionSync.aidl",
+ ":inputconstants_aidl",
],
export_shared_lib_headers: ["libbinder"],
@@ -119,6 +117,7 @@
"InputTransport.cpp",
"android/os/IInputConstants.aidl",
"android/os/IInputFlinger.aidl",
+ "android/os/InputConfig.aidl",
],
static_libs: [
"libhostgraphics",
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/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 265cbf0..5ce10a4 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -47,55 +47,6 @@
*/
const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
- @Backing(type="int")
- enum InputFeature {
- /**
- * Does not construct an input channel for this window. The channel will therefore
- * be incapable of receiving input.
- */
- NO_INPUT_CHANNEL = 0x00000002,
-
- /**
- * When this window has focus, does not call user activity for all input events so
- * the application will have to do it itself. Should only be used by
- * the keyguard and phone app.
- *
- * Should only be used by the keyguard and phone app.
- */
- DISABLE_USER_ACTIVITY = 0x00000004,
-
- /**
- * Internal flag used to indicate that input should be dropped on this window.
- */
- DROP_INPUT = 0x00000008,
-
- /**
- * Internal flag used to indicate that input should be dropped on this window if this window
- * is obscured.
- */
- DROP_INPUT_IF_OBSCURED = 0x00000010,
-
- /**
- * An input spy window. This window will receive all pointer events within its touchable
- * area, but will will not stop events from being sent to other windows below it in z-order.
- * An input event will be dispatched to all spy windows above the top non-spy window at the
- * event's coordinates.
- */
- SPY = 0x00000020,
-
- /**
- * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
- * to receive events from a stylus device within its touchable region. All other pointer
- * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
- *
- * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
- * not set.
- *
- * The window must be a trusted overlay to use this input feature.
- */
- INTERCEPTS_STYLUS = 0x00000040,
- }
-
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
}
diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl
new file mode 100644
index 0000000..6d1b396
--- /dev/null
+++ b/libs/input/android/os/InputConfig.aidl
@@ -0,0 +1,147 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+
+/**
+ * Input configurations flags used to determine the behavior of input windows.
+ * @hide
+ */
+@Backing(type="int")
+enum InputConfig {
+
+ /**
+ * The default InputConfig value with no flags set.
+ */
+ DEFAULT = 0,
+
+ /**
+ * Does not construct an input channel for this window. The channel will therefore
+ * be incapable of receiving input.
+ */
+ NO_INPUT_CHANNEL = 1 << 0,
+
+ /**
+ * Indicates that this input window is not visible, and thus will not be considered as
+ * an input target and will not obscure other windows.
+ */
+ NOT_VISIBLE = 1 << 1,
+
+ /**
+ * Indicates that this input window cannot be a focus target, and this will not
+ * receive any input events that can only be directed for the focused window, such
+ * as key events.
+ */
+ NOT_FOCUSABLE = 1 << 2,
+
+ /**
+ * Indicates that this input window cannot receive any events directed at a
+ * specific location on the screen, such as touchscreen, mouse, and stylus events.
+ * The window will not be considered as a touch target, but can still obscure other
+ * windows.
+ */
+ NOT_TOUCHABLE = 1 << 3,
+
+ /**
+ * Indicates that this window will not accept a touch event that is split between
+ * more than one window. When set:
+ * - If this window receives a DOWN event with the first pointer, all successive
+ * pointers that go down, regardless of their location on the screen, will be
+ * directed to this window;
+ * - If the DOWN event lands outside the touchable bounds of this window, no
+ * successive pointers that go down, regardless of their location on the screen,
+ * will be directed to this window.
+ */
+ PREVENT_SPLITTING = 1 << 4,
+
+ /**
+ * Indicates that this window shows the wallpaper behind it, so all touch events
+ * that it receives should also be sent to the wallpaper.
+ */
+ DUPLICATE_TOUCH_TO_WALLPAPER = 1 << 5,
+
+ /** Indicates that this the wallpaper's input window. */
+ IS_WALLPAPER = 1 << 6,
+
+ /**
+ * Indicates that input events should not be dispatched to this window. When set,
+ * input events directed towards this window will simply be dropped, and will not
+ * be dispatched to windows behind it.
+ */
+ PAUSE_DISPATCHING = 1 << 7,
+
+ /**
+ * This flag is set when the window is of a trusted type that is allowed to silently
+ * overlay other windows for the purpose of implementing the secure views feature.
+ * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+ * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+ */
+ TRUSTED_OVERLAY = 1 << 8,
+
+ /**
+ * Indicates that this window wants to listen for when there is a touch DOWN event
+ * that occurs outside its touchable bounds. When such an event occurs, this window
+ * will receive a MotionEvent with ACTION_OUTSIDE.
+ */
+ WATCH_OUTSIDE_TOUCH = 1 << 9,
+
+ /**
+ * When set, this flag allows touches to leave the current window whenever the finger
+ * moves above another window. When this happens, the window that touch has just left
+ * (the current window) will receive ACTION_CANCEL, and the window that touch has entered
+ * will receive ACTION_DOWN, and the remainder of the touch gesture will only go to the
+ * new window. Without this flag, the entire gesture is sent to the current window, even
+ * if the touch leaves the window's bounds.
+ */
+ SLIPPERY = 1 << 10,
+
+ /**
+ * When this window has focus, does not call user activity for all input events so
+ * the application will have to do it itself.
+ */
+ DISABLE_USER_ACTIVITY = 1 << 11,
+
+ /**
+ * Internal flag used to indicate that input should be dropped on this window.
+ */
+ DROP_INPUT = 1 << 12,
+
+ /**
+ * Internal flag used to indicate that input should be dropped on this window if this window
+ * is obscured.
+ */
+ DROP_INPUT_IF_OBSCURED = 1 << 13,
+
+ /**
+ * An input spy window. This window will receive all pointer events within its touchable
+ * area, but will not stop events from being sent to other windows below it in z-order.
+ * An input event will be dispatched to all spy windows above the top non-spy window at the
+ * event's coordinates.
+ */
+ SPY = 1 << 14,
+
+ /**
+ * When used with {@link #NOT_TOUCHABLE}, this window will continue to receive events from
+ * a stylus device within its touchable region. All other pointer events, such as from a
+ * mouse or touchscreen, will be dispatched to the windows behind it.
+ *
+ * This configuration has no effect when the config {@link #NOT_TOUCHABLE} is not set.
+ *
+ * It is not valid to set this configuration if {@link #TRUSTED_OVERLAY} is not set.
+ */
+ INTERCEPTS_STYLUS = 1 << 15,
+}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index a3eebdd..8240b08 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -111,6 +111,7 @@
struct {
std::mutex lock;
std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+ std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
bool registeredToDisplayManager GUARDED_BY(lock) = false;
std::atomic<nsecs_t> mLastKnownVsync = -1;
@@ -160,6 +161,7 @@
void scheduleCallbacks();
ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+ void registerStartTime() const;
std::mutex mLock;
// Protected by mLock
@@ -172,6 +174,9 @@
const sp<Looper> mLooper;
const std::thread::id mThreadId;
+
+ // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
+ static constexpr size_t kMaxStartTimes = 250;
};
static thread_local Choreographer* gChoreographer;
@@ -392,6 +397,7 @@
if (cb.vsyncCallback != nullptr) {
const ChoreographerFrameCallbackDataImpl frameCallbackData =
createFrameCallbackData(timestamp);
+ registerStartTime();
mInCallback = true;
cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
&frameCallbackData),
@@ -452,6 +458,16 @@
.choreographer = this};
}
+void Choreographer::registerStartTime() const {
+ std::scoped_lock _l(gChoreographers.lock);
+ for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
+ while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
+ gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
+ }
+ gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+}
+
} // namespace android
using namespace android;
@@ -566,6 +582,16 @@
return AChoreographer_to_Choreographer(choreographer)->getFrameInterval();
}
+int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
+ std::scoped_lock _l(gChoreographers.lock);
+ const auto iter = gChoreographers.startTimes.find(vsyncId);
+ if (iter == gChoreographers.startTimes.end()) {
+ ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
+ return 0;
+ }
+ return iter->second;
+}
+
} // namespace android
/* Glue for the NDK interface */
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 19be5bd..cdf7e66 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -71,4 +71,9 @@
int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
const AChoreographerFrameCallbackData* data, size_t index);
+// Gets the start time (dispatch time) in nanos of the callback, given a vsync ID. This does not
+// account for clients that use multiple choreographers, because callbacks that give the same vsync
+// ID may be dispatched at different times.
+int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId);
+
} // namespace android
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 2da10cd..969d937 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -42,6 +42,7 @@
android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos*;
android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos*;
+ android::AChoreographer_getStartTimeNanosForVsyncId*;
android::AChoreographer_signalRefreshRateCallbacks*;
android::AChoreographer_getFrameInterval*;
android::ADisplay_acquirePhysicalDisplays*;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 07c5dd8..84e84dd 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -122,6 +122,9 @@
":librenderengine_threaded_sources",
":librenderengine_skia_sources",
],
+ header_libs: [
+ "libtonemap_headers",
+ ],
include_dirs: [
"external/skia/src/gpu",
],
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index a426850..d91af1e 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -42,6 +42,9 @@
"libshaders",
"libtonemap",
],
+ header_libs: [
+ "libtonemap_headers",
+ ],
shared_libs: [
"libbase",
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
index 390b228..1cd143e 100644
--- a/libs/shaders/Android.bp
+++ b/libs/shaders/Android.bp
@@ -38,6 +38,10 @@
"libui-types",
],
+ header_libs: [
+ "libtonemap_headers",
+ ],
+
srcs: [
"shaders.cpp",
],
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
index 5360fe2..99d1b22 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -25,7 +25,6 @@
name: "libtonemap",
vendor_available: true,
- export_include_dirs: ["include"],
local_include_dirs: ["include"],
shared_libs: [
@@ -41,3 +40,9 @@
"tonemap.cpp",
],
}
+
+cc_library_headers {
+ name: "libtonemap_headers",
+ vendor_available: true,
+ export_include_dirs: ["include"],
+}
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index f46f3fa..d519482 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -27,6 +27,9 @@
srcs: [
"tonemap_test.cpp",
],
+ header_libs: [
+ "libtonemap_headers",
+ ],
shared_libs: [
"android.hardware.graphics.common-V3-ndk",
],
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index b34d906..42dd85e 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -134,6 +134,10 @@
return mMatrix[1][1];
}
+float Transform::det() const {
+ return mMatrix[0][0] * mMatrix[1][1] - mMatrix[0][1] * mMatrix[1][0];
+}
+
float Transform::getScaleX() const {
return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx()));
}
@@ -390,7 +394,7 @@
const float x = M[2][0];
const float y = M[2][1];
- const float idet = 1.0f / (a*d - b*c);
+ const float idet = 1.0f / det();
result.mMatrix[0][0] = d*idet;
result.mMatrix[0][1] = -c*idet;
result.mMatrix[1][0] = -b*idet;
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 33fbe05..f1178ca 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -77,6 +77,7 @@
float dtdx() const;
float dtdy() const;
float dsdy() const;
+ float det() const;
float getScaleX() const;
float getScaleY() const;
diff --git a/opengl/OWNERS b/opengl/OWNERS
index a9bd4bb..a47fb9a 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,6 +1,12 @@
+abdolrashidi@google.com
+cclao@google.com
chrisforbes@google.com
cnorthrop@google.com
ianelliott@google.com
jessehall@google.com
+lfy@google.com
lpy@google.com
timvp@google.com
+romanl@google.com
+vantablack@google.com
+yuxinhu@google.com
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index 67ce9f2..974ae38 100644
--- a/services/gpuservice/gpuwork/GpuWork.cpp
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -39,17 +39,30 @@
#include <map>
#include <mutex>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include "gpuwork/gpu_work.h"
-#define MS_IN_NS (1000000)
+#define ONE_MS_IN_NS (10000000)
namespace android {
namespace gpuwork {
namespace {
+bool lessThanGpuIdUid(const android::gpuwork::GpuIdUid& l, const android::gpuwork::GpuIdUid& r) {
+ return std::tie(l.gpu_id, l.uid) < std::tie(r.gpu_id, r.uid);
+}
+
+size_t hashGpuIdUid(const android::gpuwork::GpuIdUid& gpuIdUid) {
+ return static_cast<size_t>((gpuIdUid.gpu_id << 5U) + gpuIdUid.uid);
+}
+
+bool equalGpuIdUid(const android::gpuwork::GpuIdUid& l, const android::gpuwork::GpuIdUid& r) {
+ return std::tie(l.gpu_id, l.uid) == std::tie(r.gpu_id, r.uid);
+}
+
// Gets a BPF map from |mapPath|.
template <class Key, class Value>
bool getBpfMap(const char* mapPath, bpf::BpfMap<Key, Value>* out) {
@@ -76,24 +89,6 @@
return result;
}
-template <>
-inline int32_t cast_int32<uint64_t>(uint64_t source) {
- if (source > std::numeric_limits<int32_t>::max()) {
- return std::numeric_limits<int32_t>::max();
- }
- return static_cast<int32_t>(source);
-}
-
-template <>
-inline int32_t cast_int32<long long>(long long source) {
- if (source > std::numeric_limits<int32_t>::max()) {
- return std::numeric_limits<int32_t>::max();
- } else if (source < std::numeric_limits<int32_t>::min()) {
- return std::numeric_limits<int32_t>::min();
- }
- return static_cast<int32_t>(source);
-}
-
} // namespace
using base::StringAppendF;
@@ -115,7 +110,7 @@
{
std::scoped_lock<std::mutex> lock(mMutex);
if (mStatsdRegistered) {
- AStatsManager_clearPullAtomCallback(android::util::GPU_FREQ_TIME_IN_STATE_PER_UID);
+ AStatsManager_clearPullAtomCallback(android::util::GPU_WORK_PER_UID);
}
}
@@ -144,7 +139,7 @@
mPreviousMapClearTimePoint = std::chrono::steady_clock::now();
}
- // Attach the tracepoint ONLY if we got the map above.
+ // Attach the tracepoint.
if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power",
"gpu_work_period")) {
return;
@@ -157,8 +152,8 @@
{
std::lock_guard<std::mutex> lock(mMutex);
- AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
- nullptr, GpuWork::pullAtomCallback, this);
+ AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_WORK_PER_UID}, nullptr,
+ GpuWork::pullAtomCallback, this);
mStatsdRegistered = true;
}
@@ -169,18 +164,18 @@
void GpuWork::dump(const Vector<String16>& /* args */, std::string* result) {
if (!mInitialized.load()) {
- result->append("GPU time in state information is not available.\n");
+ result->append("GPU work information is not available.\n");
return;
}
- // Ordered map ensures output data is sorted by UID.
- std::map<Uid, UidTrackingInfo> dumpMap;
+ // Ordered map ensures output data is sorted.
+ std::map<GpuIdUid, UidTrackingInfo, decltype(lessThanGpuIdUid)*> dumpMap(&lessThanGpuIdUid);
{
std::lock_guard<std::mutex> lock(mMutex);
if (!mGpuWorkMap.isValid()) {
- result->append("GPU time in state map is not available.\n");
+ result->append("GPU work map is not available.\n");
return;
}
@@ -189,56 +184,42 @@
// threads. The buckets are all preallocated. Our eBPF program only updates
// entries (in-place) or adds entries. |GpuWork| only iterates or clears the
// map while holding |mMutex|. Given this, we should be able to iterate over
- // all elements reliably. In the worst case, we might see elements more than
- // once.
+ // all elements reliably. Nevertheless, we copy into a map to avoid
+ // duplicates.
// Note that userspace reads of BPF maps make a copy of the value, and
// thus the returned value is not being concurrently accessed by the BPF
// program (no atomic reads needed below).
- mGpuWorkMap.iterateWithValue([&dumpMap](const Uid& key, const UidTrackingInfo& value,
- const android::bpf::BpfMap<Uid, UidTrackingInfo>&)
- -> base::Result<void> {
- dumpMap[key] = value;
- return {};
- });
+ mGpuWorkMap.iterateWithValue(
+ [&dumpMap](const GpuIdUid& key, const UidTrackingInfo& value,
+ const android::bpf::BpfMap<GpuIdUid, UidTrackingInfo>&)
+ -> base::Result<void> {
+ dumpMap[key] = value;
+ return {};
+ });
}
- // Find the largest frequency where some UID has spent time in that frequency.
- size_t largestFrequencyWithTime = 0;
- for (const auto& uidToUidInfo : dumpMap) {
- for (size_t i = largestFrequencyWithTime + 1; i < kNumTrackedFrequencies; ++i) {
- if (uidToUidInfo.second.frequency_times_ns[i] > 0) {
- largestFrequencyWithTime = i;
- }
- }
- }
-
- // Dump time in state information.
+ // Dump work information.
// E.g.
- // uid/freq: 0MHz 50MHz 100MHz ...
- // 1000: 0 0 0 0 ...
- // 1003: 0 0 3456 0 ...
- // [errors:3]1006: 0 0 3456 0 ...
+ // GPU work information.
+ // gpu_id uid total_active_duration_ns total_inactive_duration_ns
+ // 0 1000 0 0
+ // 0 1003 1234 123
+ // [errors:3]0 1006 4567 456
// Header.
- result->append("GPU time in frequency state in ms.\n");
- result->append("uid/freq: 0MHz");
- for (size_t i = 1; i <= largestFrequencyWithTime; ++i) {
- StringAppendF(result, " %zuMHz", i * 50);
- }
- result->append("\n");
+ result->append("GPU work information.\ngpu_id uid total_active_duration_ns "
+ "total_inactive_duration_ns\n");
- for (const auto& uidToUidInfo : dumpMap) {
- if (uidToUidInfo.second.error_count) {
- StringAppendF(result, "[errors:%" PRIu32 "]", uidToUidInfo.second.error_count);
+ for (const auto& idToUidInfo : dumpMap) {
+ if (idToUidInfo.second.error_count) {
+ StringAppendF(result, "[errors:%" PRIu32 "]", idToUidInfo.second.error_count);
}
- StringAppendF(result, "%" PRIu32 ":", uidToUidInfo.first);
- for (size_t i = 0; i <= largestFrequencyWithTime; ++i) {
- StringAppendF(result, " %" PRIu64,
- uidToUidInfo.second.frequency_times_ns[i] / MS_IN_NS);
- }
- result->append("\n");
+ StringAppendF(result, "%" PRIu32 " %" PRIu32 " %" PRIu64 " %" PRIu64 "\n",
+ idToUidInfo.first.gpu_id, idToUidInfo.first.uid,
+ idToUidInfo.second.total_active_duration_ns,
+ idToUidInfo.second.total_inactive_duration_ns);
}
}
@@ -275,14 +256,14 @@
ATRACE_CALL();
GpuWork* gpuWork = reinterpret_cast<GpuWork*>(cookie);
- if (atomTag == android::util::GPU_FREQ_TIME_IN_STATE_PER_UID) {
- return gpuWork->pullFrequencyAtoms(data);
+ if (atomTag == android::util::GPU_WORK_PER_UID) {
+ return gpuWork->pullWorkAtoms(data);
}
return AStatsManager_PULL_SKIP;
}
-AStatsManager_PullAtomCallbackReturn GpuWork::pullFrequencyAtoms(AStatsEventList* data) {
+AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* data) {
ATRACE_CALL();
if (!data || !mInitialized.load()) {
@@ -295,96 +276,153 @@
return AStatsManager_PULL_SKIP;
}
- std::unordered_map<Uid, UidTrackingInfo> uidInfos;
+ std::unordered_map<GpuIdUid, UidTrackingInfo, decltype(hashGpuIdUid)*, decltype(equalGpuIdUid)*>
+ workMap(32, &hashGpuIdUid, &equalGpuIdUid);
// Iteration of BPF hash maps can be unreliable (no data races, but elements
// may be repeated), as the map is typically being modified by other
// threads. The buckets are all preallocated. Our eBPF program only updates
// entries (in-place) or adds entries. |GpuWork| only iterates or clears the
// map while holding |mMutex|. Given this, we should be able to iterate over
- // all elements reliably. In the worst case, we might see elements more than
- // once.
+ // all elements reliably. Nevertheless, we copy into a map to avoid
+ // duplicates.
// Note that userspace reads of BPF maps make a copy of the value, and thus
// the returned value is not being concurrently accessed by the BPF program
// (no atomic reads needed below).
- mGpuWorkMap.iterateWithValue(
- [&uidInfos](const Uid& key, const UidTrackingInfo& value,
- const android::bpf::BpfMap<Uid, UidTrackingInfo>&) -> base::Result<void> {
- uidInfos[key] = value;
- return {};
- });
-
- ALOGI("pullFrequencyAtoms: uidInfos.size() == %zu", uidInfos.size());
+ mGpuWorkMap.iterateWithValue([&workMap](const GpuIdUid& key, const UidTrackingInfo& value,
+ const android::bpf::BpfMap<GpuIdUid, UidTrackingInfo>&)
+ -> base::Result<void> {
+ workMap[key] = value;
+ return {};
+ });
// Get a list of just the UIDs; the order does not matter.
std::vector<Uid> uids;
- for (const auto& pair : uidInfos) {
- uids.push_back(pair.first);
+ // Get a list of the GPU IDs, in order.
+ std::set<uint32_t> gpuIds;
+ {
+ // To avoid adding duplicate UIDs.
+ std::unordered_set<Uid> addedUids;
+
+ for (const auto& workInfo : workMap) {
+ if (addedUids.insert(workInfo.first.uid).second) {
+ // Insertion was successful.
+ uids.push_back(workInfo.first.uid);
+ }
+ gpuIds.insert(workInfo.first.gpu_id);
+ }
}
+ ALOGI("pullWorkAtoms: uids.size() == %zu", uids.size());
+ ALOGI("pullWorkAtoms: gpuIds.size() == %zu", gpuIds.size());
+
+ if (gpuIds.size() > kNumGpusHardLimit) {
+ // If we observe a very high number of GPUs then something has probably
+ // gone wrong, so don't log any atoms.
+ return AStatsManager_PULL_SKIP;
+ }
+
+ size_t numSampledUids = kNumSampledUids;
+
+ if (gpuIds.size() > kNumGpusSoftLimit) {
+ // If we observe a high number of GPUs then we just sample 1 UID.
+ numSampledUids = 1;
+ }
+
+ // Remove all UIDs that do not have at least |kMinGpuTimeNanoseconds| on at
+ // least one GPU.
+ {
+ auto uidIt = uids.begin();
+ while (uidIt != uids.end()) {
+ bool hasEnoughGpuTime = false;
+ for (uint32_t gpuId : gpuIds) {
+ auto infoIt = workMap.find(GpuIdUid{gpuId, *uidIt});
+ if (infoIt == workMap.end()) {
+ continue;
+ }
+ if (infoIt->second.total_active_duration_ns +
+ infoIt->second.total_inactive_duration_ns >=
+ kMinGpuTimeNanoseconds) {
+ hasEnoughGpuTime = true;
+ break;
+ }
+ }
+ if (hasEnoughGpuTime) {
+ ++uidIt;
+ } else {
+ uidIt = uids.erase(uidIt);
+ }
+ }
+ }
+
+ ALOGI("pullWorkAtoms: after removing uids with very low GPU time: uids.size() == %zu",
+ uids.size());
+
std::random_device device;
std::default_random_engine random_engine(device());
- // If we have more than |kNumSampledUids| UIDs, choose |kNumSampledUids|
+ // If we have more than |numSampledUids| UIDs, choose |numSampledUids|
// random UIDs. We swap them to the front of the list. Given the list
// indices 0..i..n-1, we have the following inclusive-inclusive ranges:
// - [0, i-1] == the randomly chosen elements.
// - [i, n-1] == the remaining unchosen elements.
- if (uids.size() > kNumSampledUids) {
- for (size_t i = 0; i < kNumSampledUids; ++i) {
+ if (uids.size() > numSampledUids) {
+ for (size_t i = 0; i < numSampledUids; ++i) {
std::uniform_int_distribution<size_t> uniform_dist(i, uids.size() - 1);
size_t random_index = uniform_dist(random_engine);
std::swap(uids[i], uids[random_index]);
}
- // Only keep the front |kNumSampledUids| elements.
- uids.resize(kNumSampledUids);
+ // Only keep the front |numSampledUids| elements.
+ uids.resize(numSampledUids);
}
- ALOGI("pullFrequencyAtoms: uids.size() == %zu", uids.size());
+ ALOGI("pullWorkAtoms: after random selection: uids.size() == %zu", uids.size());
auto now = std::chrono::steady_clock::now();
-
- int32_t duration = cast_int32(
+ long long duration =
std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
- .count());
+ .count();
+ if (duration > std::numeric_limits<int32_t>::max() || duration < 0) {
+ // This is essentially impossible. If it does somehow happen, give up,
+ // but still clear the map.
+ clearMap();
+ return AStatsManager_PULL_SKIP;
+ }
- for (const Uid uid : uids) {
- const UidTrackingInfo& info = uidInfos[uid];
- ALOGI("pullFrequencyAtoms: adding stats for UID %" PRIu32, uid);
- android::util::addAStatsEvent(data, int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
- // uid
- bitcast_int32(uid),
- // time_duration_seconds
- int32_t{duration},
- // max_freq_mhz
- int32_t{1000},
- // freq_0_mhz_time_millis
- cast_int32(info.frequency_times_ns[0] / 1000000),
- // freq_50_mhz_time_millis
- cast_int32(info.frequency_times_ns[1] / 1000000),
- // ... etc. ...
- cast_int32(info.frequency_times_ns[2] / 1000000),
- cast_int32(info.frequency_times_ns[3] / 1000000),
- cast_int32(info.frequency_times_ns[4] / 1000000),
- cast_int32(info.frequency_times_ns[5] / 1000000),
- cast_int32(info.frequency_times_ns[6] / 1000000),
- cast_int32(info.frequency_times_ns[7] / 1000000),
- cast_int32(info.frequency_times_ns[8] / 1000000),
- cast_int32(info.frequency_times_ns[9] / 1000000),
- cast_int32(info.frequency_times_ns[10] / 1000000),
- cast_int32(info.frequency_times_ns[11] / 1000000),
- cast_int32(info.frequency_times_ns[12] / 1000000),
- cast_int32(info.frequency_times_ns[13] / 1000000),
- cast_int32(info.frequency_times_ns[14] / 1000000),
- cast_int32(info.frequency_times_ns[15] / 1000000),
- cast_int32(info.frequency_times_ns[16] / 1000000),
- cast_int32(info.frequency_times_ns[17] / 1000000),
- cast_int32(info.frequency_times_ns[18] / 1000000),
- cast_int32(info.frequency_times_ns[19] / 1000000),
- // freq_1000_mhz_time_millis
- cast_int32(info.frequency_times_ns[20] / 1000000));
+ // Log an atom for each (gpu id, uid) pair for which we have data.
+ for (uint32_t gpuId : gpuIds) {
+ for (Uid uid : uids) {
+ auto it = workMap.find(GpuIdUid{gpuId, uid});
+ if (it == workMap.end()) {
+ continue;
+ }
+ const UidTrackingInfo& info = it->second;
+
+ uint64_t total_active_duration_ms = info.total_active_duration_ns / ONE_MS_IN_NS;
+ uint64_t total_inactive_duration_ms = info.total_inactive_duration_ns / ONE_MS_IN_NS;
+
+ // Skip this atom if any numbers are out of range. |duration| is
+ // already checked above.
+ if (total_active_duration_ms > std::numeric_limits<int32_t>::max() ||
+ total_inactive_duration_ms > std::numeric_limits<int32_t>::max()) {
+ continue;
+ }
+
+ ALOGI("pullWorkAtoms: adding stats for GPU ID %" PRIu32 "; UID %" PRIu32, gpuId, uid);
+ android::util::addAStatsEvent(data, int32_t{android::util::GPU_WORK_PER_UID},
+ // uid
+ bitcast_int32(uid),
+ // gpu_id
+ bitcast_int32(gpuId),
+ // time_duration_seconds
+ static_cast<int32_t>(duration),
+ // total_active_duration_millis
+ static_cast<int32_t>(total_active_duration_ms),
+ // total_inactive_duration_millis
+ static_cast<int32_t>(total_inactive_duration_ms));
+ }
}
clearMap();
return AStatsManager_PULL_SUCCESS;
@@ -435,7 +473,7 @@
uint64_t numEntries = globalData.value().num_map_entries;
// If the map is <=75% full, we do nothing.
- if (numEntries <= (kMaxTrackedUids / 4) * 3) {
+ if (numEntries <= (kMaxTrackedGpuIdUids / 4) * 3) {
return;
}
@@ -456,22 +494,22 @@
// Iterating BPF maps to delete keys is tricky. If we just repeatedly call
// |getFirstKey()| and delete that, we may loop forever (or for a long time)
- // because our BPF program might be repeatedly re-adding UID keys. Also,
- // even if we limit the number of elements we try to delete, we might only
- // delete new entries, leaving old entries in the map. If we delete a key A
- // and then call |getNextKey(A)|, the first key in the map is returned, so
- // we have the same issue.
+ // because our BPF program might be repeatedly re-adding keys. Also, even if
+ // we limit the number of elements we try to delete, we might only delete
+ // new entries, leaving old entries in the map. If we delete a key A and
+ // then call |getNextKey(A)|, the first key in the map is returned, so we
+ // have the same issue.
//
// Thus, we instead get the next key and then delete the previous key. We
// also limit the number of deletions we try, just in case.
- base::Result<Uid> key = mGpuWorkMap.getFirstKey();
+ base::Result<GpuIdUid> key = mGpuWorkMap.getFirstKey();
- for (size_t i = 0; i < kMaxTrackedUids; ++i) {
+ for (size_t i = 0; i < kMaxTrackedGpuIdUids; ++i) {
if (!key.ok()) {
break;
}
- base::Result<Uid> previousKey = key;
+ base::Result<GpuIdUid> previousKey = key;
key = mGpuWorkMap.getNextKey(previousKey.value());
mGpuWorkMap.deleteValue(previousKey.value());
}
diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
index a0e1b22..c8e79a2 100644
--- a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
+++ b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
@@ -27,66 +27,124 @@
#endif
#define S_IN_NS (1000000000)
-#define MHZ_IN_KHZS (1000)
+#define SMALL_TIME_GAP_LIMIT_NS (S_IN_NS)
-typedef uint32_t Uid;
-
-// A map from UID to |UidTrackingInfo|.
-DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, Uid, UidTrackingInfo, kMaxTrackedUids, AID_GRAPHICS);
+// A map from GpuIdUid (GPU ID and application UID) to |UidTrackingInfo|.
+DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, GpuIdUid, UidTrackingInfo, kMaxTrackedGpuIdUids,
+ AID_GRAPHICS);
// A map containing a single entry of |GlobalData|.
DEFINE_BPF_MAP_GRW(gpu_work_global_data, ARRAY, uint32_t, GlobalData, 1, AID_GRAPHICS);
-// GpuUidWorkPeriodEvent defines the structure of a kernel tracepoint under the
-// tracepoint system (also referred to as the group) "power" and name
-// "gpu_work_period". In summary, the kernel tracepoint should be
-// "power/gpu_work_period", available at:
+// Defines the structure of the kernel tracepoint:
//
// /sys/kernel/tracing/events/power/gpu_work_period/
//
-// GpuUidWorkPeriodEvent defines a non-overlapping, non-zero period of time when
-// work was running on the GPU for a given application (identified by its UID;
-// the persistent, unique ID of the application) from |start_time_ns| to
-// |end_time_ns|. Drivers should issue this tracepoint as soon as possible
-// (within 1 second) after |end_time_ns|. For a given UID, periods must not
-// overlap, but periods from different UIDs can overlap (and should overlap, if
-// and only if that is how the work was executed). The period includes
-// information such as |frequency_khz|, the frequency that the GPU was running
-// at during the period, and |includes_compute_work|, whether the work included
-// compute shader work (not just graphics work). GPUs may have multiple
-// frequencies that can be adjusted, but the driver should report the frequency
-// that most closely estimates power usage (e.g. the frequency of shader cores,
-// not a scheduling unit).
+// Drivers must define an appropriate gpu_work_period kernel tracepoint (for
+// example, using the DECLARE_EVENT_CLASS and DEFINE_EVENT macros) such that the
+// arguments/fields match the fields of |GpuWorkPeriodEvent|, excluding the
+// initial "common" field. Drivers must invoke the tracepoint (also referred to
+// as emitting the event) as described below. Note that the description below
+// assumes a single physical GPU and its driver; for devices with multiple GPUs,
+// each GPU and its driver should emit events independently, using a different
+// value for |gpu_id| per GPU.
//
-// If any information changes while work from the UID is running on the GPU
-// (e.g. the GPU frequency changes, or the work starts/stops including compute
-// work) then the driver must conceptually end the period, issue the tracepoint,
-// start tracking a new period, and eventually issue a second tracepoint when
-// the work completes or when the information changes again. In this case, the
-// |end_time_ns| of the first period must equal the |start_time_ns| of the
-// second period. The driver may also end and start a new period (without a
-// gap), even if no information changes. For example, this might be convenient
-// if there is a collection of work from a UID running on the GPU for a long
-// time; ending and starting a period as individual parts of the work complete
-// allows the consumer of the tracepoint to be updated about the ongoing work.
+// |GpuWorkPeriodEvent| defines a non-overlapping, non-zero period of time from
+// |start_time_ns| (inclusive) until |end_time_ns| (exclusive) for a given
+// |uid|, and includes details of how much work the GPU was performing for |uid|
+// during the period. When GPU work for a given |uid| runs on the GPU, the
+// driver must track one or more periods that cover the time where the work was
+// running, and emit events soon after. The driver should try to emit the event
+// for a period at most 1 second after |end_time_ns|, and must emit the event at
+// most 2 seconds after |end_time_ns|. A period's duration (|end_time_ns| -
+// |start_time_ns|) must be at most 1 second. Periods for different |uids| can
+// overlap, but periods for the same |uid| must not overlap. The driver must
+// emit events for the same |uid| in strictly increasing order of
+// |start_time_ns|, such that it is guaranteed that the tracepoint call for a
+// period for |uid| has returned before the tracepoint call for the next period
+// for |uid| is made. Note that synchronization may be necessary if the driver
+// emits events for the same |uid| from different threads/contexts. Note that
+// |end_time_ns| for a period for a |uid| may equal the |start_time_ns| of the
+// next period for |uid|. The driver should try to avoid emitting a large number
+// of events in a short time period (e.g. 1000 events per second) for a given
+// |uid|.
//
-// For a given UID, the tracepoints must be emitted in order; that is, the
-// |start_time_ns| of each subsequent tracepoint for a given UID must be
-// monotonically increasing.
+// The |total_active_duration_ns| must be set to the approximate total amount of
+// time the GPU spent running work for |uid| within the period, without
+// "double-counting" parallel GPU work on the same GPU for the same |uid|. "GPU
+// work" should correspond to the "GPU slices" shown in the AGI (Android GPU
+// Inspector) tool, and so should include work such as fragment and non-fragment
+// work/shaders running on the shader cores of the GPU. For example, given the
+// following:
+// - A period has:
+// - |start_time_ns|: 100,000,000 ns
+// - |end_time_ns|: 800,000,000 ns
+// - Some GPU vertex work (A):
+// - started at: 200,000,000 ns
+// - ended at: 400,000,000 ns
+// - Some GPU fragment work (B):
+// - started at: 300,000,000 ns
+// - ended at: 500,000,000 ns
+// - Some GPU fragment work (C):
+// - started at: 300,000,000 ns
+// - ended at: 400,000,000 ns
+// - Some GPU fragment work (D):
+// - started at: 600,000,000 ns
+// - ended at: 700,000,000 ns
+//
+// The |total_active_duration_ns| would be 400,000,000 ns, because GPU work for
+// |uid| was executing:
+// - from 200,000,000 ns to 500,000,000 ns, giving a duration of 300,000,000 ns
+// (encompassing GPU work A, B, and C)
+// - from 600,000,000 ns to 700,000,000 ns, giving a duration of 100,000,000 ns
+// (GPU work D)
+//
+// Thus, the |total_active_duration_ns| is the sum of the (non-overlapping)
+// durations. Drivers may not have efficient access to the exact start and end
+// times of all GPU work, as shown above, but drivers should try to
+// approximate/aggregate the value of |total_active_duration_ns| as accurately
+// as possible within the limitations of the hardware, without double-counting
+// parallel GPU work for the same |uid|. The |total_active_duration_ns| value
+// must be less than or equal to the period duration (|end_time_ns| -
+// |start_time_ns|); if the aggregation approach might violate this requirement
+// then the driver must clamp |total_active_duration_ns| to be at most the
+// period duration.
+//
+// Note that the above description allows for a certain amount of flexibility in
+// how the driver tracks periods and emits the events. We list a few examples of
+// how drivers might implement the above:
+//
+// - 1: The driver could track periods for all |uid| values at fixed intervals
+// of 1 second. Thus, every period duration would be exactly 1 second, and
+// periods from different |uid|s that overlap would have the same
+// |start_time_ns| and |end_time_ns| values.
+//
+// - 2: The driver could track periods with many different durations (up to 1
+// second), as needed in order to cover the GPU work for each |uid|.
+// Overlapping periods for different |uid|s may have very different durations,
+// as well as different |start_time_ns| and |end_time_ns| values.
+//
+// - 3: The driver could track fine-grained periods with different durations
+// that precisely cover the time where GPU work is running for each |uid|.
+// Thus, |total_active_duration_ns| would always equal the period duration.
+// For example, if a game was running at 60 frames per second, the driver
+// would most likely emit _at least_ 60 events per second (probably more, as
+// there would likely be multiple "chunks" of GPU work per frame, with gaps
+// between each chunk). However, the driver may sometimes need to resort to
+// more coarse-grained periods to avoid emitting thousands of events per
+// second for a |uid|, where |total_active_duration_ns| would then be less
+// than the period duration.
typedef struct {
// Actual fields start at offset 8.
- uint64_t reserved;
+ uint64_t common;
- // The UID of the process (i.e. persistent, unique ID of the Android app)
- // that submitted work to the GPU.
+ // A value that uniquely identifies the GPU within the system.
+ uint32_t gpu_id;
+
+ // The UID of the application (i.e. persistent, unique ID of the Android
+ // app) that submitted work to the GPU.
uint32_t uid;
- // The GPU frequency during the period. GPUs may have multiple frequencies
- // that can be adjusted, but the driver should report the frequency that
- // would most closely estimate power usage (e.g. the frequency of shader
- // cores, not a scheduling unit).
- uint32_t frequency_khz;
-
// The start time of the period in nanoseconds. The clock must be
// CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
uint64_t start_time_ns;
@@ -95,54 +153,44 @@
// CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
uint64_t end_time_ns;
- // Flags about the work. Reserved for future use.
- uint64_t flags;
+ // The amount of time the GPU was running GPU work for |uid| during the
+ // period, in nanoseconds, without double-counting parallel GPU work for the
+ // same |uid|. For example, this might include the amount of time the GPU
+ // spent performing shader work (vertex work, fragment work, etc.) for
+ // |uid|.
+ uint64_t total_active_duration_ns;
- // The maximum GPU frequency allowed during the period according to the
- // thermal throttling policy. Must be 0 if no thermal throttling was
- // enforced during the period. The value must relate to |frequency_khz|; in
- // other words, if |frequency_khz| is the frequency of the GPU shader cores,
- // then |thermally_throttled_max_frequency_khz| must be the maximum allowed
- // frequency of the GPU shader cores (not the maximum allowed frequency of
- // some other part of the GPU).
- //
- // Note: Unlike with other fields of this struct, if the
- // |thermally_throttled_max_frequency_khz| value conceptually changes while
- // work from a UID is running on the GPU then the GPU driver does NOT need
- // to accurately report this by ending the period and starting to track a
- // new period; instead, the GPU driver may report any one of the
- // |thermally_throttled_max_frequency_khz| values that was enforced during
- // the period. The motivation for this relaxation is that we assume the
- // maximum frequency will not be changing rapidly, and so capturing the
- // exact point at which the change occurs is unnecessary.
- uint32_t thermally_throttled_max_frequency_khz;
+} GpuWorkPeriodEvent;
-} GpuUidWorkPeriodEvent;
-
-_Static_assert(offsetof(GpuUidWorkPeriodEvent, uid) == 8 &&
- offsetof(GpuUidWorkPeriodEvent, frequency_khz) == 12 &&
- offsetof(GpuUidWorkPeriodEvent, start_time_ns) == 16 &&
- offsetof(GpuUidWorkPeriodEvent, end_time_ns) == 24 &&
- offsetof(GpuUidWorkPeriodEvent, flags) == 32 &&
- offsetof(GpuUidWorkPeriodEvent, thermally_throttled_max_frequency_khz) == 40,
- "Field offsets of struct GpuUidWorkPeriodEvent must not be changed because they "
+_Static_assert(offsetof(GpuWorkPeriodEvent, gpu_id) == 8 &&
+ offsetof(GpuWorkPeriodEvent, uid) == 12 &&
+ offsetof(GpuWorkPeriodEvent, start_time_ns) == 16 &&
+ offsetof(GpuWorkPeriodEvent, end_time_ns) == 24 &&
+ offsetof(GpuWorkPeriodEvent, total_active_duration_ns) == 32,
+ "Field offsets of struct GpuWorkPeriodEvent must not be changed because they "
"must match the tracepoint field offsets found via adb shell cat "
"/sys/kernel/tracing/events/power/gpu_work_period/format");
DEFINE_BPF_PROG("tracepoint/power/gpu_work_period", AID_ROOT, AID_GRAPHICS, tp_gpu_work_period)
-(GpuUidWorkPeriodEvent* const args) {
- // Note: In BPF programs, |__sync_fetch_and_add| is translated to an atomic
+(GpuWorkPeriodEvent* const period) {
+ // Note: In eBPF programs, |__sync_fetch_and_add| is translated to an atomic
// add.
- const uint32_t uid = args->uid;
+ // Return 1 to avoid blocking simpleperf from receiving events.
+ const int ALLOW = 1;
- // Get |UidTrackingInfo| for |uid|.
- UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+ GpuIdUid gpu_id_and_uid;
+ __builtin_memset(&gpu_id_and_uid, 0, sizeof(gpu_id_and_uid));
+ gpu_id_and_uid.gpu_id = period->gpu_id;
+ gpu_id_and_uid.uid = period->uid;
+
+ // Get |UidTrackingInfo|.
+ UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&gpu_id_and_uid);
if (!uid_tracking_info) {
// There was no existing entry, so we add a new one.
UidTrackingInfo initial_info;
__builtin_memset(&initial_info, 0, sizeof(initial_info));
- if (0 == bpf_gpu_work_map_update_elem(&uid, &initial_info, BPF_NOEXIST)) {
+ if (0 == bpf_gpu_work_map_update_elem(&gpu_id_and_uid, &initial_info, BPF_NOEXIST)) {
// We added an entry to the map, so we increment our entry counter in
// |GlobalData|.
const uint32_t zero = 0;
@@ -154,66 +202,80 @@
__sync_fetch_and_add(&global_data->num_map_entries, 1);
}
}
- uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+ uid_tracking_info = bpf_gpu_work_map_lookup_elem(&gpu_id_and_uid);
if (!uid_tracking_info) {
// This should never happen, unless entries are getting deleted at
// this moment. If so, we just give up.
- return 0;
+ return ALLOW;
}
}
- // The period duration must be non-zero.
- if (args->start_time_ns >= args->end_time_ns) {
+ if (
+ // The period duration must be non-zero.
+ period->start_time_ns >= period->end_time_ns ||
+ // The period duration must be at most 1 second.
+ (period->end_time_ns - period->start_time_ns) > S_IN_NS) {
__sync_fetch_and_add(&uid_tracking_info->error_count, 1);
- return 0;
+ return ALLOW;
}
- // The frequency must not be 0.
- if (args->frequency_khz == 0) {
- __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
- return 0;
+ // If |total_active_duration_ns| is 0 then no GPU work occurred and there is
+ // nothing to do.
+ if (period->total_active_duration_ns == 0) {
+ return ALLOW;
}
- // Calculate the frequency index: see |UidTrackingInfo.frequency_times_ns|.
- // Round to the nearest 50MHz bucket.
- uint32_t frequency_index =
- ((args->frequency_khz / MHZ_IN_KHZS) + (kFrequencyGranularityMhz / 2)) /
- kFrequencyGranularityMhz;
- if (frequency_index >= kNumTrackedFrequencies) {
- frequency_index = kNumTrackedFrequencies - 1;
- }
+ // Update |uid_tracking_info->total_active_duration_ns|.
+ __sync_fetch_and_add(&uid_tracking_info->total_active_duration_ns,
+ period->total_active_duration_ns);
- // Never round down to 0MHz, as this is a special bucket (see below) and not
- // an actual operating point.
- if (frequency_index == 0) {
- frequency_index = 1;
- }
-
- // Update time in state.
- __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[frequency_index],
- args->end_time_ns - args->start_time_ns);
-
- if (uid_tracking_info->previous_end_time_ns > args->start_time_ns) {
- // This must not happen because per-UID periods must not overlap and
- // must be emitted in order.
+ // |small_gap_time_ns| is the time gap between the current and previous
+ // active period, which could be 0. If the gap is more than
+ // |SMALL_TIME_GAP_LIMIT_NS| then |small_gap_time_ns| will be set to 0
+ // because we want to estimate the small gaps between "continuous" GPU work.
+ uint64_t small_gap_time_ns = 0;
+ if (uid_tracking_info->previous_active_end_time_ns > period->start_time_ns) {
+ // The current period appears to have occurred before the previous
+ // active period, which must not happen because per-UID periods must not
+ // overlap and must be emitted in strictly increasing order of
+ // |start_time_ns|.
__sync_fetch_and_add(&uid_tracking_info->error_count, 1);
} else {
- // The period appears to have been emitted after the previous, as
- // expected, so we can calculate the gap between this and the previous
- // period.
- const uint64_t gap_time = args->start_time_ns - uid_tracking_info->previous_end_time_ns;
+ // The current period appears to have been emitted after the previous
+ // active period, as expected, so we can calculate the gap between the
+ // current and previous active period.
+ small_gap_time_ns = period->start_time_ns - uid_tracking_info->previous_active_end_time_ns;
- // Update |previous_end_time_ns|.
- uid_tracking_info->previous_end_time_ns = args->end_time_ns;
+ // Update |previous_active_end_time_ns|.
+ uid_tracking_info->previous_active_end_time_ns = period->end_time_ns;
- // Update the special 0MHz frequency time, which stores the gaps between
- // periods, but only if the gap is < 1 second.
- if (gap_time > 0 && gap_time < S_IN_NS) {
- __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[0], gap_time);
+ // We want to estimate the small gaps between "continuous" GPU work; if
+ // the gap is more than |SMALL_TIME_GAP_LIMIT_NS| then we don't consider
+ // this "continuous" GPU work.
+ if (small_gap_time_ns > SMALL_TIME_GAP_LIMIT_NS) {
+ small_gap_time_ns = 0;
}
}
- return 0;
+ uint64_t period_total_inactive_time_ns = 0;
+ const uint64_t period_duration_ns = period->end_time_ns - period->start_time_ns;
+ // |period->total_active_duration_ns| is the active time within the period duration, so
+ // it must not be larger than |period_duration_ns|.
+ if (period->total_active_duration_ns > period_duration_ns) {
+ __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+ } else {
+ period_total_inactive_time_ns = period_duration_ns - period->total_active_duration_ns;
+ }
+
+ // Update |uid_tracking_info->total_inactive_duration_ns| by adding the
+ // inactive time from this period, plus the small gap between the current
+ // and previous active period. Either or both of these values could be 0.
+ if (small_gap_time_ns > 0 || period_total_inactive_time_ns > 0) {
+ __sync_fetch_and_add(&uid_tracking_info->total_inactive_duration_ns,
+ small_gap_time_ns + period_total_inactive_time_ns);
+ }
+
+ return ALLOW;
}
LICENSE("Apache 2.0");
diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
index 4fe8464..57338f4 100644
--- a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
+++ b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
@@ -25,18 +25,28 @@
namespace gpuwork {
#endif
+typedef struct {
+ uint32_t gpu_id;
+ uint32_t uid;
+} GpuIdUid;
+
typedef struct {
- // The end time of the previous period from this UID in nanoseconds.
- uint64_t previous_end_time_ns;
+ // The end time of the previous period where the GPU was active for the UID,
+ // in nanoseconds.
+ uint64_t previous_active_end_time_ns;
- // The time spent at each GPU frequency while running GPU work from the UID,
- // in nanoseconds. Array index i stores the time for frequency i*50 MHz. So
- // index 0 is 0Mhz, index 1 is 50MHz, index 2 is 100MHz, etc., up to index
- // |kNumTrackedFrequencies|.
- uint64_t frequency_times_ns[21];
+ // The total amount of time the GPU has spent running work for the UID, in
+ // nanoseconds.
+ uint64_t total_active_duration_ns;
- // The number of times we received |GpuUidWorkPeriodEvent| events in an
- // unexpected order. See |GpuUidWorkPeriodEvent|.
+ // The total amount of time of the "gaps" between "continuous" GPU work for
+ // the UID, in nanoseconds. This is estimated by ignoring large gaps between
+ // GPU work for this UID.
+ uint64_t total_inactive_duration_ns;
+
+ // The number of errors detected due to |GpuWorkPeriodEvent| events for the
+ // UID violating the specification in some way. E.g. periods with a zero or
+ // negative duration.
uint32_t error_count;
} UidTrackingInfo;
@@ -48,14 +58,10 @@
uint64_t num_map_entries;
} GlobalData;
-static const uint32_t kMaxTrackedUids = 512;
-static const uint32_t kFrequencyGranularityMhz = 50;
-static const uint32_t kNumTrackedFrequencies = 21;
+// The maximum number of tracked GPU ID and UID pairs (|GpuIdUid|).
+static const uint32_t kMaxTrackedGpuIdUids = 512;
#ifdef __cplusplus
-static_assert(kNumTrackedFrequencies ==
- std::extent<decltype(UidTrackingInfo::frequency_times_ns)>::value);
-
} // namespace gpuwork
} // namespace android
#endif
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
index b6f493d..acecd56 100644
--- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -41,7 +41,7 @@
void initialize();
- // Dumps the GPU time in frequency state information.
+ // Dumps the GPU work information.
void dump(const Vector<String16>& args, std::string* result);
private:
@@ -55,7 +55,7 @@
AStatsEventList* data,
void* cookie);
- AStatsManager_PullAtomCallbackReturn pullFrequencyAtoms(AStatsEventList* data);
+ AStatsManager_PullAtomCallbackReturn pullWorkAtoms(AStatsEventList* data);
// Periodically calls |clearMapIfNeeded| to clear the |mGpuWorkMap| map, if
// needed.
@@ -88,7 +88,7 @@
std::mutex mMutex;
// BPF map for per-UID GPU work.
- bpf::BpfMap<Uid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex);
+ bpf::BpfMap<GpuIdUid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex);
// BPF map containing a single element for global data.
bpf::BpfMap<uint32_t, GlobalData> mGpuWorkGlobalDataMap GUARDED_BY(mMutex);
@@ -110,7 +110,18 @@
bool mStatsdRegistered GUARDED_BY(mMutex) = false;
// The number of randomly chosen (i.e. sampled) UIDs to log stats for.
- static constexpr int kNumSampledUids = 10;
+ static constexpr size_t kNumSampledUids = 10;
+
+ // A "large" number of GPUs. If we observe more GPUs than this limit then
+ // we reduce the amount of stats we log.
+ static constexpr size_t kNumGpusSoftLimit = 4;
+
+ // A "very large" number of GPUs. If we observe more GPUs than this limit
+ // then we don't log any stats.
+ static constexpr size_t kNumGpusHardLimit = 32;
+
+ // The minimum GPU time needed to actually log stats for a UID.
+ static constexpr uint64_t kMinGpuTimeNanoseconds = 30U * 1000000000U; // 30 seconds.
// The previous time point at which |mGpuWorkMap| was cleared.
std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex);
diff --git a/services/gpuservice/vts/AndroidTest.xml b/services/gpuservice/vts/AndroidTest.xml
index 02ca07f..34dc2ea 100644
--- a/services/gpuservice/vts/AndroidTest.xml
+++ b/services/gpuservice/vts/AndroidTest.xml
@@ -14,6 +14,9 @@
limitations under the License.
-->
<configuration description="Runs GpuServiceVendorTests">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="false" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="jar" value="GpuServiceVendorTests.jar" />
</test>
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
index 4a77d9a..9fa9016 100644
--- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -78,12 +78,11 @@
"\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;",
"\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;",
"\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;",
- "\tfield:u32 uid;\toffset:8;\tsize:4;\tsigned:0;",
- "\tfield:u32 frequency;\toffset:12;\tsize:4;\tsigned:0;",
- "\tfield:u64 start_time;\toffset:16;\tsize:8;\tsigned:0;",
- "\tfield:u64 end_time;\toffset:24;\tsize:8;\tsigned:0;",
- "\tfield:u64 flags;\toffset:32;\tsize:8;\tsigned:0;",
- "\tfield:u32 thermally_throttled_max_frequency_khz;\toffset:40;\tsize:4;\tsigned:0;"
+ "\tfield:u32 gpu_id;\toffset:8;\tsize:4;\tsigned:0;",
+ "\tfield:u32 uid;\toffset:12;\tsize:4;\tsigned:0;",
+ "\tfield:u64 start_time_ns;\toffset:16;\tsize:8;\tsigned:0;",
+ "\tfield:u64 end_time_ns;\toffset:24;\tsize:8;\tsigned:0;",
+ "\tfield:u64 total_active_duration_ns;\toffset:32;\tsize:8;\tsigned:0;"
);
// We use |fail| rather than |assertEquals| because it allows us to give a clearer message.
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..06ad6a8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -66,35 +66,77 @@
namespace {
-// Log detailed debug messages about each inbound event notification to the dispatcher.
-constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false;
+/**
+ * Log detailed debug messages about each inbound event notification to the dispatcher.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG" (requires restart)
+ */
+const bool DEBUG_INBOUND_EVENT_DETAILS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
-// Log detailed debug messages about each outbound event processed by the dispatcher.
-constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false;
+/**
+ * Log detailed debug messages about each outbound event processed by the dispatcher.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherOutboundEvent DEBUG" (requires restart)
+ */
+const bool DEBUG_OUTBOUND_EVENT_DETAILS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundEvent", ANDROID_LOG_INFO);
-// Log debug messages about the dispatch cycle.
-constexpr bool DEBUG_DISPATCH_CYCLE = false;
+/**
+ * Log debug messages about the dispatch cycle.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherDispatchCycle DEBUG" (requires restart)
+ */
+const bool DEBUG_DISPATCH_CYCLE =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "DispatchCycle", ANDROID_LOG_INFO);
-// Log debug messages about channel creation
-constexpr bool DEBUG_CHANNEL_CREATION = false;
+/**
+ * Log debug messages about channel creation
+ * Enable this via "adb shell setprop log.tag.InputDispatcherChannelCreation DEBUG" (requires
+ * restart)
+ */
+const bool DEBUG_CHANNEL_CREATION =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "ChannelCreation", ANDROID_LOG_INFO);
-// Log debug messages about input event injection.
-constexpr bool DEBUG_INJECTION = false;
+/**
+ * Log debug messages about input event injection.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherInjection DEBUG" (requires restart)
+ */
+const bool DEBUG_INJECTION =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Injection", ANDROID_LOG_INFO);
-// Log debug messages about input focus tracking.
-constexpr bool DEBUG_FOCUS = false;
+/**
+ * Log debug messages about input focus tracking.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart)
+ */
+const bool DEBUG_FOCUS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Focus", ANDROID_LOG_INFO);
-// Log debug messages about touch mode event
-constexpr bool DEBUG_TOUCH_MODE = false;
+/**
+ * Log debug messages about touch mode event
+ * Enable this via "adb shell setprop log.tag.InputDispatcherTouchMode DEBUG" (requires restart)
+ */
+const bool DEBUG_TOUCH_MODE =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchMode", ANDROID_LOG_INFO);
-// Log debug messages about touch occlusion
-constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+/**
+ * Log debug messages about touch occlusion
+ * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires
+ * restart)
+ */
+const bool DEBUG_TOUCH_OCCLUSION =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO);
-// Log debug messages about the app switch latency optimization.
-constexpr bool DEBUG_APP_SWITCH = false;
+/**
+ * Log debug messages about the app switch latency optimization.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherAppSwitch DEBUG" (requires restart)
+ */
+const bool DEBUG_APP_SWITCH =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "AppSwitch", ANDROID_LOG_INFO);
-// Log debug messages about hover events.
-constexpr bool DEBUG_HOVER = false;
+/**
+ * Log debug messages about hover events.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherHover DEBUG" (requires restart)
+ */
+const bool DEBUG_HOVER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
// Temporarily releases a held mutex for the lifetime of the instance.
// Named to match std::scoped_lock
@@ -118,10 +160,7 @@
// when an application takes too long to respond and the user has pressed an app switch key.
constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
-// Amount of time to allow for an event to be dispatched (measured since its eventTime)
-// before considering it stale and dropping it.
-const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL // 10sec
- * HwTimeoutMultiplier();
+const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier();
// Log a warning when an event takes longer than this to process, even if an ANR does not occur.
constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
@@ -322,10 +361,6 @@
first->applicationInfo.token == second->applicationInfo.token;
}
-bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
- return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
-}
-
std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
std::shared_ptr<EventEntry> eventEntry,
int32_t inputTargetFlags) {
@@ -526,6 +561,10 @@
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
+ : InputDispatcher(policy, STALE_EVENT_TIMEOUT) {}
+
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+ std::chrono::nanoseconds staleEventTimeout)
: mPolicy(policy),
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
@@ -544,6 +583,7 @@
mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
+ mStaleEventTimeout(staleEventTimeout),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator) {
mLooper = new Looper(false);
@@ -901,6 +941,10 @@
}
}
+bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+ return std::chrono::nanoseconds(currentTime - entry.eventTime) >= mStaleEventTimeout;
+}
+
/**
* Return true if the events preceding this incoming motion event should be dropped
* Return false otherwise (the default behaviour)
@@ -2698,18 +2742,16 @@
std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
bool isTouchedWindow) const {
- return StringPrintf(INDENT2
- "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
- "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
- "], touchableRegion=%s, window={%s}, inputConfig={%s}, inputFeatures={%s}, "
- "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
+ return StringPrintf(INDENT2 "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+ "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+ "], touchableRegion=%s, window={%s}, inputConfig={%s}, "
+ "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(),
info->alpha, info->frameLeft, info->frameTop, info->frameRight,
info->frameBottom, dumpRegion(info->touchableRegion).c_str(),
info->name.c_str(), info->inputConfig.string().c_str(),
- info->inputFeatures.string().c_str(), toString(info->token != nullptr),
- info->applicationInfo.name.c_str(),
+ toString(info->token != nullptr), info->applicationInfo.name.c_str(),
toString(info->applicationInfo.token).c_str());
}
@@ -2787,7 +2829,7 @@
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
if (focusedWindowHandle != nullptr) {
const WindowInfo* info = focusedWindowHandle->getInfo();
- if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
+ if (info->inputConfig.test(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY)) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
}
@@ -4426,19 +4468,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]);
}
}
@@ -4526,7 +4558,7 @@
bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
const bool noInputChannel =
- windowHandle.getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+ windowHandle.getInfo()->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
if (connection != nullptr && noInputChannel) {
ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
@@ -4576,7 +4608,7 @@
const WindowInfo* info = handle->getInfo();
if (getInputChannelLocked(handle->getToken()) == nullptr) {
const bool noInputChannel =
- info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+ info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
const bool canReceiveInput =
!info->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) ||
!info->inputConfig.test(WindowInfo::InputConfig::NOT_FOCUSABLE);
@@ -4642,7 +4674,7 @@
const WindowInfo& info = *window->getInfo();
// Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
- const bool noInputWindow = info.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+ const bool noInputWindow = info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
if (noInputWindow && window->getToken() != nullptr) {
ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
window->getName().c_str());
@@ -5219,8 +5251,6 @@
windowInfo->applicationInfo.name.c_str(),
toString(windowInfo->applicationInfo.token).c_str());
dump += dumpRegion(windowInfo->touchableRegion);
- dump += StringPrintf(", inputFeatures=%s",
- windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
"ms, hasToken=%s, "
"touchOcclusionMode=%s\n",
@@ -5816,35 +5846,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 +5873,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 +5895,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,
@@ -6274,13 +6288,14 @@
bool InputDispatcher::shouldDropInput(
const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
- if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) ||
- (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) &&
+ if (windowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::DROP_INPUT) ||
+ (windowHandle->getInfo()->inputConfig.test(
+ WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) &&
isWindowObscuredLocked(windowHandle))) {
- ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
- "%" PRId32 ".",
+ ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on "
+ "display %" PRId32 ".",
ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
- windowHandle->getInfo()->inputFeatures.string().c_str(),
+ windowHandle->getInfo()->inputConfig.string().c_str(),
windowHandle->getInfo()->displayId);
return true;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e162c78..3c79c98 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -84,6 +84,8 @@
static constexpr bool kDefaultInTouchMode = true;
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+ explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+ std::chrono::nanoseconds staleEventTimeout);
~InputDispatcher() override;
void dump(std::string& dump) override;
@@ -471,6 +473,11 @@
*/
std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
+ // Amount of time to allow for an event to be dispatched (measured since its eventTime)
+ // before considering it stale and dropping it.
+ const std::chrono::nanoseconds mStaleEventTimeout;
+ bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
+
bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
/**
@@ -503,11 +510,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..9633932 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -53,6 +53,11 @@
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t SECOND_DISPLAY_ID = 1;
+static constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_1_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
// An arbitrary injector pid / uid pair that has permission to inject events.
static const int32_t INJECTOR_PID = 999;
static const int32_t INJECTOR_UID = 1001;
@@ -60,6 +65,8 @@
// An arbitrary pid of the gesture monitor window
static constexpr int32_t MONITOR_PID = 2001;
+static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+
struct PointF {
float x;
float y;
@@ -88,6 +95,8 @@
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
InputDispatcherConfiguration mConfig;
+ using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>;
+
protected:
virtual ~FakeInputDispatcherPolicy() {}
@@ -161,122 +170,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 +302,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 +311,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();
}
@@ -493,7 +491,7 @@
void SetUp() override {
mFakePolicy = new FakeInputDispatcherPolicy();
- mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
+ mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy, STALE_EVENT_TIMEOUT);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
// Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
@@ -587,11 +585,10 @@
// Rejects pointer down with invalid index.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
- AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
+ POINTER_1_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -612,11 +609,10 @@
// Rejects pointer up with invalid index.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
- AMOTION_EVENT_ACTION_POINTER_UP |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
+ POINTER_1_UP, 0, 0, edgeFlags, metaState, 0, classification, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -1000,7 +996,7 @@
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
mInfo.displayId = displayId;
- mInfo.inputConfig = WindowInfo::InputConfig::NONE;
+ mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
}
sp<FakeWindowHandle> clone(
@@ -1044,6 +1040,24 @@
mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
}
+ void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); }
+
+ void setInterceptsStylus(bool interceptsStylus) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
+ }
+
+ void setDropInput(bool dropInput) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput);
+ }
+
+ void setDropInputIfObscured(bool dropInputIfObscured) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
+ }
+
+ void setNoInputChannel(bool noInputChannel) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
+ }
+
void setAlpha(float alpha) { mInfo.alpha = alpha; }
void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
@@ -1074,8 +1088,6 @@
mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
}
- void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; }
-
void setTrustedOverlay(bool trustedOverlay) {
mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
}
@@ -1228,7 +1240,7 @@
void assertNoEvents() {
if (mInputReceiver == nullptr &&
- mInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
+ mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
return; // Can't receive events if the window does not have input channel
}
ASSERT_NE(nullptr, mInputReceiver)
@@ -1245,6 +1257,8 @@
mInfo.ownerUid = ownerUid;
}
+ int32_t getPid() const { return mInfo.ownerPid; }
+
void destroyReceiver() { mInputReceiver = nullptr; }
int getChannelFd() { return mInputReceiver->getChannelFd(); }
@@ -1503,6 +1517,10 @@
return args;
}
+static NotifyMotionArgs generateTouchArgs(int32_t action, const std::vector<PointF>& points) {
+ return generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, points);
+}
+
static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
}
@@ -1738,9 +1756,7 @@
// Second finger down on the top window
const MotionEvent secondFingerDownEvent =
- MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
.x(100)
@@ -1803,9 +1819,7 @@
// Second finger down on the right window
const MotionEvent secondFingerDownEvent =
- MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
.x(100)
@@ -1853,6 +1867,61 @@
wallpaperWindow->assertNoEvents();
}
+/**
+ * On the display, have a single window, and also an area where there's no window.
+ * First pointer touches the "no window" area of the screen. Second pointer touches the window.
+ * Make sure that the window receives the second pointer, and first pointer is simply ignored.
+ */
+TEST_F(InputDispatcherTest, SplitWorksWhenEmptyAreaIsTouched) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", DISPLAY_ID);
+
+ mDispatcher->setInputWindows({{DISPLAY_ID, {window}}});
+ NotifyMotionArgs args;
+
+ // Touch down on the empty space
+ mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{-1, -1}})));
+
+ mDispatcher->waitForIdle();
+ window->assertNoEvents();
+
+ // Now touch down on the window with another pointer
+ mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{-1, -1}, {10, 10}})));
+ mDispatcher->waitForIdle();
+ window->consumeMotionDown();
+}
+
+/**
+ * Same test as above, but instead of touching the empty space, the first touch goes to
+ * non-touchable window.
+ */
+TEST_F(InputDispatcherTest, SplitWorksWhenNonTouchableWindowIsTouched) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window1 =
+ new FakeWindowHandle(application, mDispatcher, "Window1", DISPLAY_ID);
+ window1->setTouchableRegion(Region{{0, 0, 100, 100}});
+ window1->setTouchable(false);
+ sp<FakeWindowHandle> window2 =
+ new FakeWindowHandle(application, mDispatcher, "Window2", DISPLAY_ID);
+ window2->setTouchableRegion(Region{{100, 0, 200, 100}});
+
+ mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
+
+ NotifyMotionArgs args;
+ // Touch down on the non-touchable window
+ mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}})));
+
+ mDispatcher->waitForIdle();
+ window1->assertNoEvents();
+ window2->assertNoEvents();
+
+ // Now touch down on the window with another pointer
+ mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
+ mDispatcher->waitForIdle();
+ window2->consumeMotionDown();
+}
+
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
@@ -2323,9 +2392,7 @@
// Send pointer down to the first window
NotifyMotionArgs pointerDownMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{touchPoint, touchPoint});
mDispatcher->notifyMotion(&pointerDownMotionArgs);
// Only the first window should get the pointer down event
@@ -2343,9 +2410,7 @@
// Send pointer up to the second window
NotifyMotionArgs pointerUpMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{touchPoint, touchPoint});
mDispatcher->notifyMotion(&pointerUpMotionArgs);
// The first window gets nothing and the second gets pointer up
@@ -2405,9 +2470,7 @@
// Send down to the second window
NotifyMotionArgs secondDownMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{pointInFirst, pointInSecond});
mDispatcher->notifyMotion(&secondDownMotionArgs);
// The first window gets a move and the second a down
@@ -2422,9 +2485,7 @@
// Send pointer up to the second window
NotifyMotionArgs pointerUpMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{pointInFirst, pointInSecond});
mDispatcher->notifyMotion(&pointerUpMotionArgs);
// The first window gets nothing and the second gets pointer up
@@ -2473,9 +2534,7 @@
// Send down to the second window
NotifyMotionArgs secondDownMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{pointInFirst, pointInSecond});
mDispatcher->notifyMotion(&secondDownMotionArgs);
// The first window gets a move and the second a down
@@ -2492,9 +2551,7 @@
// The rest of the dispatch should proceed as normal
// Send pointer up to the second window
NotifyMotionArgs pointerUpMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{pointInFirst, pointInSecond});
mDispatcher->notifyMotion(&pointerUpMotionArgs);
// The first window gets MOVE and the second gets pointer up
@@ -2711,9 +2768,7 @@
// Send down to the second window
NotifyMotionArgs secondDownMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{pointInFirst, pointInSecond});
mDispatcher->notifyMotion(&secondDownMotionArgs);
// The first window gets a move and the second a down
@@ -2722,9 +2777,7 @@
// Send pointer cancel to the second window
NotifyMotionArgs pointerUpMotionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{pointInFirst, pointInSecond});
pointerUpMotionArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
mDispatcher->notifyMotion(&pointerUpMotionArgs);
@@ -4180,22 +4233,18 @@
touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
- int32_t actionPointerDown =
- AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
touchedPoints.push_back(PointF{150, 150});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+ touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
// Release Window 2
- int32_t actionPointerUp =
- AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints);
expectedPoints.pop_back();
// Update the transform so rotation is set for Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+ touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
@@ -4207,12 +4256,10 @@
touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
- int32_t actionPointerDown =
- AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
touchedPoints.push_back(PointF{150, 150});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+ touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
@@ -4222,15 +4269,13 @@
touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
// Release Window 2
- int32_t actionPointerUp =
- AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints);
expectedPoints.pop_back();
// Touch Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+ touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
@@ -4249,12 +4294,10 @@
touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
- int32_t actionPointerDown =
- AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
touchedPoints.push_back(PointF{150, 150});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+ touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
@@ -4308,7 +4351,7 @@
new FakeWindowHandle(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
spy->setTrustedOverlay(true);
spy->setFocusable(false);
- spy->setInputFeatures(WindowInfo::Feature::SPY);
+ spy->setSpy(true);
spy->setDispatchingTimeout(30ms);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, mWindow}}});
return spy;
@@ -4364,13 +4407,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 +4423,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());
}
@@ -4410,6 +4453,38 @@
ASSERT_TRUE(mDispatcher->waitForIdle());
}
+/**
+ * Make sure the stale key is dropped before causing an ANR. So even if there's no focused window,
+ * there will not be an ANR.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) {
+ mWindow->setFocusable(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ KeyEvent event;
+ const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC) -
+ std::chrono::nanoseconds(STALE_EVENT_TIMEOUT).count();
+
+ // Define a valid key down event that is stale (too old).
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+ INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
+ AMETA_NONE, 1 /*repeatCount*/, eventTime, eventTime);
+
+ const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+
+ InputEventInjectionResult result =
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ InputEventInjectionSync::WAIT_FOR_RESULT,
+ INJECT_EVENT_TIMEOUT, policyFlags);
+ ASSERT_EQ(InputEventInjectionResult::FAILED, result)
+ << "Injection should fail because the event is stale";
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+ mWindow->assertNoEvents();
+}
+
// We have a focused application, but no focused window
// Make sure that we don't notify policy twice about the same ANR.
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
@@ -4485,7 +4560,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 +4575,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 +4596,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 +4605,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 +4622,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 +4631,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 +4649,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 +4670,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 +4695,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 +4705,7 @@
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
mWindow->assertNoEvents();
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -4792,7 +4867,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 +4878,8 @@
mFocusedWindow->assertNoEvents();
mUnfocusedWindow->assertNoEvents();
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(),
+ mFocusedWindow->getPid());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -4817,8 +4893,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 +4910,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 +4936,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 +4957,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();
}
@@ -4981,17 +5060,13 @@
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
// Touch Window 2
- int32_t actionPointerDown =
- AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-
- motionArgs =
- generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
+ motionArgs = generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
mDispatcher->notifyMotion(&motionArgs);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
mUnfocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionDown();
@@ -5010,7 +5085,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();
@@ -5096,7 +5172,7 @@
"Window without input channel", ADISPLAY_ID_DEFAULT,
std::make_optional<sp<IBinder>>(nullptr) /*token*/);
- mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL);
+ mNoInputWindow->setNoInputChannel(true);
mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
// It's perfectly valid for this window to not have an associated input channel
@@ -5138,7 +5214,7 @@
"Window with input channel and NO_INPUT_CHANNEL",
ADISPLAY_ID_DEFAULT);
- mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL);
+ mNoInputWindow->setNoInputChannel(true);
mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
@@ -6053,7 +6129,7 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
- window->setInputFeatures(WindowInfo::Feature::DROP_INPUT);
+ window->setDropInput(true);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -6072,7 +6148,7 @@
window->assertNoEvents();
// With the flag cleared, the window should get input
- window->setInputFeatures({});
+ window->setDropInput(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
@@ -6098,7 +6174,7 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
- window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+ window->setDropInputIfObscured(true);
window->setOwnerInfo(222, 222);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
@@ -6118,7 +6194,7 @@
window->assertNoEvents();
// With the flag cleared, the window should get input
- window->setInputFeatures({});
+ window->setDropInputIfObscured(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
@@ -6144,7 +6220,7 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
- window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+ window->setDropInputIfObscured(true);
window->setOwnerInfo(222, 222);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
@@ -6253,7 +6329,7 @@
name += std::to_string(mSpyCount++);
sp<FakeWindowHandle> spy =
new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT);
- spy->setInputFeatures(WindowInfo::Feature::SPY);
+ spy->setSpy(true);
spy->setTrustedOverlay(true);
return spy;
}
@@ -6511,9 +6587,7 @@
// Second finger down on the window and spy, but the window should not receive the pointer down.
const MotionEvent secondFingerDownEvent =
- MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.displayId(ADISPLAY_ID_DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
@@ -6571,9 +6645,7 @@
spy->consumeMotionDown();
const MotionEvent secondFingerDownEvent =
- MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
.pointer(
@@ -6606,9 +6678,7 @@
spyRight->assertNoEvents();
const MotionEvent secondFingerDownEvent =
- MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
.pointer(
@@ -6647,9 +6717,7 @@
// Second finger down on window, the window should receive touch down.
const MotionEvent secondFingerDownEvent =
- MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
- (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.displayId(ADISPLAY_ID_DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
@@ -6701,7 +6769,7 @@
overlay->setFocusable(false);
overlay->setOwnerInfo(111, 111);
overlay->setTouchable(false);
- overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS);
+ overlay->setInterceptsStylus(true);
overlay->setTrustedOverlay(true);
std::shared_ptr<FakeApplicationHandle> application =
@@ -6767,7 +6835,7 @@
TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) {
auto [overlay, window] = setupStylusOverlayScenario();
- overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY);
+ overlay->setSpy(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index df4db19..455a1e2 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -42,4 +42,7 @@
srcs: [
"LatencyTrackerFuzzer.cpp",
],
+ fuzz_config: {
+ cc: ["android-framework-input@google.com"],
+ },
}
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/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 18a6bae..0aca24a 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -311,6 +311,7 @@
? 0
: mBufferInfo.mBufferSlot;
compositionState->acquireFence = mBufferInfo.mFence;
+ compositionState->frameNumber = mBufferInfo.mFrameNumber;
compositionState->sidebandStreamHasFrame = false;
}
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/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 669eaad..8a696f1 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -141,6 +141,8 @@
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
uint64_t mPreviousReleasedFrameNumber = 0;
+ uint64_t mPreviousBarrierFrameNumber = 0;
+
bool mReleasePreviousBuffer = false;
// Stores the last set acquire fence signal time used to populate the callback handle's acquire
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/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8bf7f8f..283fe86 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -166,6 +166,7 @@
int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
sp<Fence> acquireFence = Fence::NO_FENCE;
Region surfaceDamage;
+ uint64_t frameNumber = 0;
// The handle to use for a sideband stream for this layer
sp<NativeHandle> sidebandStream;
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 c7984bd..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,7 +77,6 @@
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&);
@@ -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/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 66dd825..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;
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/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 14324de..cb00e71 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -28,6 +28,7 @@
#include "DisplayHardware/Hal.h"
#include "math/HashCombine.h"
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
namespace std {
@@ -395,6 +396,21 @@
return std::vector<std::string>{base::StringPrintf("%p", p)};
}};
+ static auto constexpr BufferEquals = [](const wp<GraphicBuffer>& lhs,
+ const wp<GraphicBuffer>& rhs) -> bool {
+ // Avoid a promotion if the wp<>'s aren't equal
+ if (lhs != rhs) return false;
+
+ // Even if the buffer didn't change, check to see if we need to act as if the buffer changed
+ // anyway. Specifically, look to see if the buffer is FRONT_BUFFER & if so act as if it's
+ // always different
+ using ::aidl::android::hardware::graphics::common::BufferUsage;
+ sp<GraphicBuffer> promotedBuffer = lhs.promote();
+ return !(promotedBuffer &&
+ ((promotedBuffer->getUsage() & static_cast<int64_t>(BufferUsage::FRONT_BUFFER)) !=
+ 0));
+ };
+
OutputLayerState<wp<GraphicBuffer>, LayerStateField::Buffer>
mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
[](const wp<GraphicBuffer>& buffer) {
@@ -403,7 +419,14 @@
base::StringPrintf("%p",
promotedBuffer ? promotedBuffer.get()
: nullptr)};
- }};
+ },
+ BufferEquals};
+
+ // Even if the same buffer is passed to BLAST's setBuffer(), we still increment the frame
+ // number and need to treat it as if the buffer changed. Otherwise we break existing
+ // front-buffer rendering paths (such as egl's EGL_SINGLE_BUFFER).
+ OutputLayerState<uint64_t, LayerStateField::Buffer> mFrameNumber{
+ [](auto layer) { return layer->getLayerFE().getCompositionState()->frameNumber; }};
int64_t mFramesSinceBufferUpdate = 0;
@@ -453,7 +476,7 @@
return hash;
}};
- static const constexpr size_t kNumNonUniqueFields = 16;
+ static const constexpr size_t kNumNonUniqueFields = 17;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -472,6 +495,7 @@
&mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
&mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
&mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
+ &mFrameNumber,
};
}
};
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 6390025..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,8 +260,13 @@
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);
@@ -287,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(),
@@ -390,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
@@ -400,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 ff332eb..cd10bc1 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;
@@ -1085,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,
@@ -1092,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);
}
@@ -1112,8 +1200,12 @@
// because high frequency consumes extra battery.
const bool expensiveBlurs =
refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
- const bool expensiveRenderingExpected =
- clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 || expensiveBlurs;
+ const bool expensiveRenderingExpected = expensiveBlurs ||
+ std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+ [outputDataspace =
+ clientCompositionDisplay.outputDataspace](const auto& layer) {
+ return layer.sourceDataspace != outputDataspace;
+ });
if (expensiveRenderingExpected) {
setExpensiveRenderingExpected(true);
}
@@ -1349,12 +1441,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 {
@@ -1369,5 +1462,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/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 3e983f3..723593d 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -325,7 +325,8 @@
// For hdr content, treat the white point as the display brightness - HDR content should not be
// boosted or dimmed.
if (isHdrDataspace(state.dataspace) ||
- getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits) {
+ getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
+ getOutput().getState().displayBrightnessNits == 0.f) {
state.dimmingRatio = 1.f;
state.whitePointNits = getOutput().getState().displayBrightnessNits;
} else {
@@ -506,9 +507,18 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
- // Don't dim cached layers
- const auto dimmingRatio =
- outputDependentState.overrideInfo.buffer ? 1.f : outputDependentState.dimmingRatio;
+ // Cached layers are not dimmed, which means that composer should attempt to dim.
+ // Note that if the dimming ratio is large, then this may cause the cached layer
+ // to kick back into GPU composition :(
+ // Also note that this assumes that there are no HDR layers that are able to be cached.
+ // Otherwise, this could cause HDR layers to be dimmed twice.
+ const auto dimmingRatio = outputDependentState.overrideInfo.buffer
+ ? (getOutput().getState().displayBrightnessNits != 0.f
+ ? std::clamp(getOutput().getState().sdrWhitePointNits /
+ getOutput().getState().displayBrightnessNits,
+ 0.f, 1.f)
+ : 1.f)
+ : outputDependentState.dimmingRatio;
if (auto error = hwcLayer->setBrightness(dimmingRatio); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(),
@@ -788,6 +798,7 @@
}};
settings.sourceDataspace = getState().overrideInfo.dataspace;
settings.alpha = 1.0f;
+ settings.whitePointNits = getOutput().getState().sdrWhitePointNits;
return {static_cast<LayerFE::LayerSettings>(settings)};
}
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 cd03235..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);
@@ -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/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dda0822..8eb1946 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -657,7 +657,7 @@
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
}
-TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) {
+TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsAndDimmingRatioCorrectly) {
mOutputState.sdrWhitePointNits = 200.f;
mOutputState.displayBrightnessNits = 800.f;
@@ -665,12 +665,15 @@
mLayerFEState.isColorspaceAgnostic = false;
mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(mOutputState.sdrWhitePointNits, mOutputLayer.getState().whitePointNits);
+ EXPECT_EQ(mOutputState.sdrWhitePointNits / mOutputState.displayBrightnessNits,
+ mOutputLayer.getState().dimmingRatio);
mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
mLayerFEState.isColorspaceAgnostic = false;
mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+ EXPECT_EQ(1.f, mOutputLayer.getState().dimmingRatio);
}
TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
@@ -750,9 +753,10 @@
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
static constexpr float kWhitePointNits = 200.f;
+ static constexpr float kSdrWhitePointNits = 100.f;
static constexpr float kDisplayBrightnessNits = 400.f;
static constexpr float kLayerBrightness = kWhitePointNits / kDisplayBrightnessNits;
- static constexpr float kFullLayerBrightness = 1.f;
+ static constexpr float kOverrideLayerBrightness = kSdrWhitePointNits / kDisplayBrightnessNits;
static const half4 kColor;
static const Rect kDisplayFrame;
@@ -798,6 +802,7 @@
mLayerFEState.acquireFence = kFence;
mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
+ mOutputState.sdrWhitePointNits = kSdrWhitePointNits;
EXPECT_CALL(mOutput, getDisplayColorProfile())
.WillRepeatedly(Return(&mDisplayColorProfile));
@@ -1117,7 +1122,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kSkipAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage, kFullLayerBrightness);
+ kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1133,7 +1138,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kSkipAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage, kFullLayerBrightness);
+ kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1149,7 +1154,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage, kFullLayerBrightness);
+ kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1165,7 +1170,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage, kFullLayerBrightness);
+ kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c2521b2..af172d8 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));
}
/*
@@ -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>();
}
};
@@ -3641,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) {
@@ -3649,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) {
@@ -3661,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) {
@@ -3683,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) {
@@ -3693,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) {
@@ -3704,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) {
@@ -3715,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) {
@@ -3726,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 {
@@ -3744,8 +3982,9 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
mOutput.mState.dataspace = kExpensiveOutputDataspace;
+ LayerFE::LayerSettings layerSettings;
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kExpensiveOutputDataspace, _))
- .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
+ .WillOnce(Return(std::vector<LayerFE::LayerSettings>{layerSettings}));
// For this test, we also check the call order of key functions.
InSequence seq;
@@ -3755,7 +3994,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
@@ -3790,7 +4033,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) {
@@ -3800,7 +4048,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/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 0b1c262..bd4ff13 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "LayerStateTest"
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/mock/LayerFE.h>
@@ -29,7 +30,8 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
-using aidl::android::hardware::graphics::composer3::Composition;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::composer3::Composition;
namespace android::compositionengine::impl::planner {
namespace {
@@ -359,6 +361,54 @@
EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
}
+TEST_F(LayerStateTest, updateBufferSingleBufferedLegacy) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+
+ for (uint64_t i = 0; i < 10; i++) {
+ layerFECompositionStateTwo.frameNumber = i;
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+ }
+}
+
+TEST_F(LayerStateTest, updateBufferSingleBufferedUsage) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ layerFECompositionStateTwo.buffer->usage = static_cast<uint64_t>(BufferUsage::FRONT_BUFFER);
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+
+ for (uint64_t i = 0; i < 10; i++) {
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+ }
+}
+
TEST_F(LayerStateTest, compareBuffer) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
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/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 5b2e265..a8d439b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -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/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..aeaf1e1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -338,6 +338,12 @@
// Calculate effective layer transform
mEffectiveTransform = parentTransform * getActiveTransform(s);
+ if (CC_UNLIKELY(!isTransformValid())) {
+ ALOGW("Stop computing bounds for %s because it has invalid transformation.",
+ getDebugName());
+ return;
+ }
+
// Transform parent bounds to layer space
parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
@@ -1326,6 +1332,10 @@
}
}
}
+ if (CC_UNLIKELY(!isTransformValid())) {
+ ALOGW("Hide layer %s because it has invalid transformation.", getDebugName());
+ return true;
+ }
return s.flags & layer_state_t::eLayerHidden;
}
@@ -1858,6 +1868,11 @@
return mEffectiveTransform;
}
+bool Layer::isTransformValid() const {
+ float transformDet = getTransform().det();
+ return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet);
+}
+
half Layer::getAlpha() const {
const auto& p = mDrawingParent.promote();
@@ -2142,6 +2157,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 {
@@ -2248,14 +2266,14 @@
}
void Layer::handleDropInputMode(gui::WindowInfo& info) const {
- if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
+ if (mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
return;
}
// Check if we need to drop input unconditionally
gui::DropInputMode dropInputMode = getDropInputMode();
if (dropInputMode == gui::DropInputMode::ALL) {
- info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
ALOGV("Dropping input for %s as requested by policy.", getDebugName());
return;
}
@@ -2268,7 +2286,7 @@
// Check if the parent has set an alpha on the layer
sp<Layer> parent = mDrawingParent.promote();
if (parent && parent->getAlpha() != 1.0_hf) {
- info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
static_cast<float>(getAlpha()));
}
@@ -2276,7 +2294,7 @@
// Check if the parent has cropped the buffer
Rect bufferSize = getCroppedBufferSize(getDrawingState());
if (!bufferSize.isValid()) {
- info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+ info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
return;
}
@@ -2288,7 +2306,7 @@
bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};
if (croppedByParent) {
- info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
getDebugName());
} else {
@@ -2296,7 +2314,7 @@
// input if the window is obscured. This check should be done in surfaceflinger but the
// logic currently resides in inputflinger. So pass the if_obscured check to input to only
// drop input events if the window is obscured.
- info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+ info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
}
}
@@ -2305,7 +2323,7 @@
mDrawingState.inputInfo.name = getName();
mDrawingState.inputInfo.ownerUid = mOwnerUid;
mDrawingState.inputInfo.ownerPid = mOwnerPid;
- mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
+ mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL;
mDrawingState.inputInfo.displayId = getLayerStack().id;
}
@@ -2333,7 +2351,7 @@
// If the window will be blacked out on a display because the display does not have the secure
// flag and the layer has the secure flag set, then drop input.
if (!displayIsSecure && isSecure()) {
- info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
}
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 21dd5f4..2b5e337 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -752,6 +752,7 @@
FrameEventHistoryDelta* outDelta);
ui::Transform getTransform() const;
+ bool isTransformValid() const;
// Returns the Alpha of the Surface, accounting for the Alpha
// of parent Surfaces in the hierarchy (alpha's will be multiplied
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 43a6e55..2ece51c 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -3,4 +3,5 @@
chaviw@google.com
lpy@google.com
racarr@google.com
-vishnun@google.com
+scroggo@google.com
+vishnun@google.com
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 712cd5b..f2af85e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -52,7 +52,7 @@
return;
}
- compositor.composite(frameTime);
+ compositor.composite(frameTime, mVsyncId);
compositor.sample();
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 9532e26..4082e26 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -35,7 +35,7 @@
struct ICompositor {
virtual bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) = 0;
- virtual void composite(nsecs_t frameTime) = 0;
+ virtual void composite(nsecs_t frameTime, int64_t vsyncId) = 0;
virtual void sample() = 0;
protected:
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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2cd34e9..efaa975 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) {
@@ -2185,8 +2197,8 @@
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-void SurfaceFlinger::composite(nsecs_t frameTime) {
- ATRACE_CALL();
+void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) {
+ ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
if (mPowerHintSessionData.sessionEnabled) {
mPowerHintSessionData.compositeStart = systemTime();
@@ -2376,6 +2388,19 @@
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");
@@ -2461,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) {
@@ -2583,6 +2608,13 @@
// Find the largest width and height among all the displays.
int32_t maxDisplayWidth = 0;
int32_t maxDisplayHeight = 0;
+
+ // If there are no displays, set a valid display bounds so we can still compute a valid layer
+ // bounds.
+ if (ON_MAIN_THREAD(mDisplays.size()) == 0) {
+ maxDisplayWidth = maxDisplayHeight = 5000;
+ }
+
for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
const auto& displayDevice = pair.second;
int32_t width = displayDevice->getWidth();
@@ -3647,75 +3679,102 @@
return false;
}
+int SurfaceFlinger::flushPendingTransactionQueues(
+ std::vector<TransactionState>& transactions,
+ std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
+ std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
+ bool tryApplyUnsignaled) {
+ int transactionsPendingBarrier = 0;
+ 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;
+ }
+ if (ready == TransactionReadiness::NotReadyBarrier) {
+ transactionsPendingBarrier++;
+ setTransactionFlags(eTransactionFlushNeeded);
+ break;
+ }
+ transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ const bool frameNumberChanged =
+ state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+ if (frameNumberChanged) {
+ bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
+ } else {
+ // Barrier function only used for BBQ which always includes a frame number
+ bufferLayersReadyToPresent[state.surface] =
+ std::numeric_limits<uint64_t>::max();
+ }
+ });
+ 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);
+ }
+ }
+ return transactionsPendingBarrier;
+}
+
bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
// states) around outside the scope of the lock
std::vector<TransactionState> transactions;
// Layer handles that have transactions with buffers that are ready to be applied.
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> bufferLayersReadyToPresent;
+ std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
{
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);
- }
+ int lastTransactionsPendingBarrier = 0;
+ int transactionsPendingBarrier = 0;
+ // 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.
+ transactionsPendingBarrier =
+ 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;
}
@@ -3724,25 +3783,61 @@
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
bufferLayersReadyToPresent,
- transactions.size());
+ transactions.size(),
+ /*tryApplyUnsignaled*/ false);
}();
ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
- if (ready == TransactionReadiness::NotReady) {
+ if (ready != TransactionReadiness::Ready) {
+ if (ready == TransactionReadiness::NotReadyBarrier) {
+ transactionsPendingBarrier++;
+ }
mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
- bufferLayersReadyToPresent.insert(state.surface);
- });
- const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
- if (appliedUnsignaled) {
- applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
- }
+ const bool frameNumberChanged =
+ state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+ if (frameNumberChanged) {
+ bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
+ } else {
+ // Barrier function only used for BBQ which always includes a frame number.
+ // This value only used for barrier logic.
+ bufferLayersReadyToPresent[state.surface] =
+ std::numeric_limits<uint64_t>::max();
+ }
+ });
transactions.emplace_back(std::move(transaction));
}
mTransactionQueue.pop_front();
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
+ // Transactions with a buffer pending on a barrier may be on a different applyToken
+ // than the transaction which satisfies our barrier. In fact this is the exact use case
+ // that the primitive is designed for. This means we may first process
+ // the barrier dependent transaction, determine it ineligible to complete
+ // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+ // The barrier dependent transaction was eligible to be presented in this frame
+ // but we would have prevented it without case. To fix this we continually
+ // loop through flushPendingTransactionQueues until we perform an iteration
+ // where the number of transactionsPendingBarrier doesn't change. This way
+ // we can continue to resolve dependency chains of barriers as far as possible.
+ while (lastTransactionsPendingBarrier != transactionsPendingBarrier) {
+ lastTransactionsPendingBarrier = transactionsPendingBarrier;
+ transactionsPendingBarrier =
+ flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+ applyTokensWithUnsignaledTransactions,
+ /*tryApplyUnsignaled*/ false);
+ }
+
+ // 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,8 +3944,9 @@
auto SurfaceFlinger::transactionIsReadyToBeApplied(
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 {
+ const std::unordered_map<
+ sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
+ 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
@@ -3887,7 +3983,18 @@
continue;
}
- const bool allowLatchUnsignaled =
+ if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
+ ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
+ const bool willApplyBarrierFrame =
+ (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
+ (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
+ if (!willApplyBarrierFrame) {
+ ATRACE_NAME("NotReadyBarrier");
+ return TransactionReadiness::NotReadyBarrier;
+ }
+ }
+
+ const bool allowLatchUnsignaled = tryApplyUnsignaled &&
shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
allowLatchUnsignaled ? "true" : "false");
@@ -3907,8 +4014,8 @@
if (s.hasBufferChanges()) {
// If backpressure is enabled and we already have a buffer to commit, keep the
// transaction in the queue.
- const bool hasPendingBuffer =
- bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
+ const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
+ bufferLayersReadyToPresent.end();
if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
ATRACE_NAME("hasPendingBuffer");
return TransactionReadiness::NotReady;
@@ -3921,24 +4028,6 @@
void SurfaceFlinger::queueTransaction(TransactionState& state) {
Mutex::Autolock _l(mQueueLock);
- // If its TransactionQueue already has a pending TransactionState or if it is pending
- auto itr = mPendingTransactionQueues.find(state.applyToken);
- // if this is an animation frame, wait until prior animation frame has
- // been applied by SF
- if (state.flags & eAnimation) {
- while (itr != mPendingTransactionQueues.end()) {
- status_t err =
- mTransactionQueueCV.waitRelative(mQueueLock, mAnimationTransactionTimeout);
- if (CC_UNLIKELY(err != NO_ERROR)) {
- ALOGW_IF(err == TIMED_OUT,
- "setTransactionState timed out "
- "waiting for animation frame to apply");
- break;
- }
- itr = mPendingTransactionQueues.find(state.applyToken);
- }
- }
-
// Generate a CountDownLatch pending state if this is a synchronous transaction.
if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
@@ -5473,9 +5562,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: {
@@ -5510,8 +5596,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) {
@@ -5541,6 +5626,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
@@ -6037,7 +6127,6 @@
static bool updateOverlay =
property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
if (!updateOverlay) return;
- if (Mutex::Autolock lock(mStateLock); !isRefreshRateOverlayEnabled()) return;
// Update the overlay on the main thread to avoid race conditions with
// mRefreshRateConfigs->getCurrentRefreshRate()
@@ -6047,6 +6136,7 @@
ALOGW("%s: default display is null", __func__);
return;
}
+ if (!display->isRefreshRateOverlayEnabled()) return;
const auto desiredActiveMode = display->getDesiredActiveMode();
const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
@@ -7189,6 +7279,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..86c8333 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*)
@@ -661,7 +671,7 @@
// Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
// via RenderEngine and the Composer HAL, respectively.
- void composite(nsecs_t frameTime) override;
+ void composite(nsecs_t frameTime, int64_t vsyncId) override;
// Samples the composited frame via RegionSamplingThread.
void sample() override;
@@ -750,6 +760,12 @@
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
+ int flushPendingTransactionQueues(
+ std::vector<TransactionState>& transactions,
+ std::unordered_map<sp<IBinder>, uint64_t, 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);
@@ -773,14 +789,16 @@
void commitOffscreenLayers();
enum class TransactionReadiness {
NotReady,
+ NotReadyBarrier,
Ready,
ReadyUnsignaled,
};
TransactionReadiness transactionIsReadyToBeApplied(
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);
+ const std::unordered_map<
+ sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
+ 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 +1138,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 +1188,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 +1422,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/LocklessStack.h b/services/surfaceflinger/Tracing/LocklessStack.h
new file mode 100644
index 0000000..20f2aa8
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LocklessStack.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <atomic>
+
+template <typename T>
+// Single consumer multi producer stack. We can understand the two operations independently to see
+// why they are without race condition.
+//
+// push is responsible for maintaining a linked list stored in mPush, and called from multiple
+// threads without lock. We can see that if two threads never observe the same value from
+// mPush.load, it just functions as a normal linked list. In the case where two threads observe the
+// same value, one of them has to execute the compare_exchange first. The one that doesn't execute
+// the compare exchange first, will receive false from compare_exchange. previousHead is updated (by
+// compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to
+// see that the process can repeat with an arbitrary number of threads.
+//
+// Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges
+// the entire push list with null. This is safe, since the only other reader (push)
+// of mPush will retry if it changes in between it's read and atomic compare. We
+// then store the list and pop one element.
+//
+// If we already had something in the pop list we just pop directly.
+class LocklessStack {
+public:
+ class Entry {
+ public:
+ T* mValue;
+ std::atomic<Entry*> mNext;
+ Entry(T* value): mValue(value) {}
+ };
+ std::atomic<Entry*> mPush = nullptr;
+ std::atomic<Entry*> mPop = nullptr;
+ void push(T* value) {
+ Entry* entry = new Entry(value);
+ Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
+ do {
+ entry->mNext = previousHead;
+ } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
+ }
+ T* pop() {
+ Entry* popped = mPop.load(/*std::memory_order_acquire*/);
+ if (popped) {
+ // Single consumer so this is fine
+ mPop.store(popped->mNext/* , std::memory_order_release */);
+ auto value = popped->mValue;
+ delete popped;
+ return value;
+ } else {
+ Entry *grabbedList = mPush.exchange(nullptr/* , std::memory_order_acquire */);
+ if (!grabbedList) return nullptr;
+ mPop.store(grabbedList->mNext/* , std::memory_order_release */);
+ auto value = grabbedList->mValue;
+ delete grabbedList;
+ return value;
+ }
+ }
+};
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 1e5c3e7..d249b60 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -180,13 +180,13 @@
}
if (layer.what & layer_state_t::eReparent) {
- int32_t layerId = layer.parentSurfaceControlForChild
+ int64_t layerId = layer.parentSurfaceControlForChild
? mMapper->getLayerId(layer.parentSurfaceControlForChild->getHandle())
: -1;
proto.set_parent_id(layerId);
}
if (layer.what & layer_state_t::eRelativeLayerChanged) {
- int32_t layerId = layer.relativeLayerSurfaceControl
+ int64_t layerId = layer.relativeLayerSurfaceControl
? mMapper->getLayerId(layer.relativeLayerSurfaceControl->getHandle())
: -1;
proto.set_relative_parent_id(layerId);
@@ -342,13 +342,13 @@
outState.merge(state);
if (state.what & layer_state_t::eReparent) {
- outState.parentId = proto.parent_id();
+ outState.parentId = static_cast<int32_t>(proto.parent_id());
}
if (state.what & layer_state_t::eRelativeLayerChanged) {
- outState.relativeParentId = proto.relative_parent_id();
+ outState.relativeParentId = static_cast<int32_t>(proto.relative_parent_id());
}
if (state.what & layer_state_t::eInputInfoChanged) {
- outState.inputCropId = proto.window_info_handle().crop_layer_id();
+ outState.inputCropId = static_cast<int32_t>(proto.window_info_handle().crop_layer_id());
}
if (state.what & layer_state_t::eBufferChanged) {
const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
@@ -364,7 +364,7 @@
}
void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_state_t& layer) {
- layer.layerId = proto.layer_id();
+ layer.layerId = (int32_t)proto.layer_id();
layer.what |= proto.what();
layer.surface = mMapper->getLayerHandle(layer.layerId);
@@ -431,6 +431,7 @@
layer.bufferData->frameNumber = bufferProto.frame_number();
layer.bufferData->flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
+ layer.bufferData->acquireFence = Fence::NO_FENCE;
}
if (proto.what() & layer_state_t::eApiChanged) {
@@ -450,23 +451,25 @@
}
if (proto.what() & layer_state_t::eReparent) {
- int32_t layerId = proto.parent_id();
+ int64_t layerId = proto.parent_id();
if (layerId == -1) {
layer.parentSurfaceControlForChild = nullptr;
} else {
layer.parentSurfaceControlForChild =
new SurfaceControl(SurfaceComposerClient::getDefault(),
- mMapper->getLayerHandle(layerId), nullptr, layerId);
+ mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
+ nullptr, static_cast<int32_t>(layerId));
}
}
if (proto.what() & layer_state_t::eRelativeLayerChanged) {
- int32_t layerId = proto.relative_parent_id();
+ int64_t layerId = proto.relative_parent_id();
if (layerId == -1) {
layer.relativeLayerSurfaceControl = nullptr;
} else {
layer.relativeLayerSurfaceControl =
new SurfaceControl(SurfaceComposerClient::getDefault(),
- mMapper->getLayerHandle(layerId), nullptr, layerId);
+ mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
+ nullptr, static_cast<int32_t>(layerId));
}
layer.z = proto.z();
}
@@ -493,8 +496,9 @@
inputInfo.transform.set(transformProto.tx(), transformProto.ty());
inputInfo.replaceTouchableRegionWithCrop =
windowInfoProto.replace_touchable_region_with_crop();
- int32_t layerId = windowInfoProto.crop_layer_id();
- inputInfo.touchableRegionCropHandle = mMapper->getLayerHandle(layerId);
+ int64_t layerId = windowInfoProto.crop_layer_id();
+ inputInfo.touchableRegionCropHandle =
+ mMapper->getLayerHandle(static_cast<int32_t>(layerId));
layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
}
if (proto.what() & layer_state_t::eBackgroundColorChanged) {
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 2f70b27..872a901 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -80,7 +80,8 @@
public:
virtual ~FlingerDataMapper() = default;
virtual sp<IBinder> getLayerHandle(int32_t /* layerId */) const { return nullptr; }
- virtual int32_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
+ virtual int64_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
+ virtual int64_t getLayerId(BBinder* /* layerHandle */) const { return -1; }
virtual sp<IBinder> getDisplayHandle(int32_t /* displayId */) const { return nullptr; }
virtual int32_t getDisplayId(const sp<IBinder>& /* displayHandle */) const { return -1; }
virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
@@ -106,6 +107,7 @@
TransactionState fromProto(const proto::TransactionState&);
void mergeFromProto(const proto::LayerState&, TracingLayerState& outState);
void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+ std::unique_ptr<FlingerDataMapper> mMapper;
private:
proto::LayerState toProto(const layer_state_t&);
@@ -113,7 +115,6 @@
void fromProto(const proto::LayerState&, layer_state_t& out);
DisplayState fromProto(const proto::DisplayState&);
- std::unique_ptr<FlingerDataMapper> mMapper;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index d5e837f..6381758 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -29,23 +29,16 @@
namespace android {
-class FlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
- std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
-
+// Keeps the binder address as the layer id so we can avoid holding the tracing lock in the
+// binder thread.
+class FlatDataMapper : public TransactionProtoParser::FlingerDataMapper {
public:
- FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
- : mLayerHandles(layerHandles) {}
-
- int32_t getLayerId(const sp<IBinder>& layerHandle) const override {
+ virtual int64_t getLayerId(const sp<IBinder>& layerHandle) const {
if (layerHandle == nullptr) {
return -1;
}
- auto it = mLayerHandles.find(layerHandle->localBinder());
- if (it == mLayerHandles.end()) {
- ALOGW("Could not find layer handle %p", layerHandle->localBinder());
- return -1;
- }
- return it->second;
+
+ return reinterpret_cast<int64_t>(layerHandle->localBinder());
}
void getGraphicBufferPropertiesFromCache(client_cache_t cachedBuffer, uint64_t* outBufferId,
@@ -72,8 +65,33 @@
}
};
+class FlingerDataMapper : public FlatDataMapper {
+ std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
+
+public:
+ FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
+ : mLayerHandles(layerHandles) {}
+
+ int64_t getLayerId(const sp<IBinder>& layerHandle) const override {
+ if (layerHandle == nullptr) {
+ return -1;
+ }
+ return getLayerId(layerHandle->localBinder());
+ }
+
+ int64_t getLayerId(BBinder* localBinder) const {
+ auto it = mLayerHandles.find(localBinder);
+ if (it == mLayerHandles.end()) {
+ ALOGW("Could not find layer handle %p", localBinder);
+ return -1;
+ }
+ return it->second;
+ }
+};
+
TransactionTracing::TransactionTracing()
- : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)) {
+ : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)),
+ mLockfreeProtoParser(std::make_unique<FlatDataMapper>()) {
std::scoped_lock lock(mTraceLock);
mBuffer.setSize(mBufferSizeInBytes);
@@ -129,9 +147,9 @@
}
void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
- std::scoped_lock lock(mTraceLock);
- ATRACE_CALL();
- mQueuedTransactions[transaction.id] = mProtoParser.toProto(transaction);
+ proto::TransactionState* state =
+ new proto::TransactionState(mLockfreeProtoParser.toProto(transaction));
+ mTransactionQueue.push(state);
}
void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
@@ -182,6 +200,38 @@
std::scoped_lock lock(mTraceLock);
std::vector<std::string> removedEntries;
proto::TransactionTraceEntry entryProto;
+
+ while (auto incomingTransaction = mTransactionQueue.pop()) {
+ auto transaction = *incomingTransaction;
+ int32_t layerCount = transaction.layer_changes_size();
+ for (int i = 0; i < layerCount; i++) {
+ auto layer = transaction.mutable_layer_changes(i);
+ layer->set_layer_id(
+ mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(layer->layer_id())));
+ if ((layer->what() & layer_state_t::eReparent) && layer->parent_id() != -1) {
+ layer->set_parent_id(
+ mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
+ layer->parent_id())));
+ }
+
+ if ((layer->what() & layer_state_t::eRelativeLayerChanged) &&
+ layer->relative_parent_id() != -1) {
+ layer->set_relative_parent_id(
+ mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
+ layer->relative_parent_id())));
+ }
+
+ if (layer->has_window_info_handle() &&
+ layer->window_info_handle().crop_layer_id() != -1) {
+ auto input = layer->mutable_window_info_handle();
+ input->set_crop_layer_id(
+ mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
+ input->crop_layer_id())));
+ }
+ }
+ mQueuedTransactions[incomingTransaction->transaction_id()] = transaction;
+ delete incomingTransaction;
+ }
for (const CommittedTransactions& entry : committedTransactions) {
entryProto.set_elapsed_realtime_nanos(entry.timestamp);
entryProto.set_vsync_id(entry.vsyncId);
@@ -317,9 +367,9 @@
// Merge layer states to starting transaction state.
for (const proto::TransactionState& transaction : removedEntry.transactions()) {
for (const proto::LayerState& layerState : transaction.layer_changes()) {
- auto it = mStartingStates.find(layerState.layer_id());
+ auto it = mStartingStates.find((int32_t)layerState.layer_id());
if (it == mStartingStates.end()) {
- ALOGW("Could not find layer id %d", layerState.layer_id());
+ ALOGW("Could not find layer id %d", (int32_t)layerState.layer_id());
continue;
}
mProtoParser.mergeFromProto(layerState, it->second);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 95256c4..4c291f9 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -26,6 +26,7 @@
#include <thread>
#include "RingBuffer.h"
+#include "LocklessStack.h"
#include "TransactionProtoParser.h"
using namespace android::surfaceflinger;
@@ -78,6 +79,7 @@
size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
GUARDED_BY(mTraceLock);
+ LocklessStack<proto::TransactionState> mTransactionQueue;
nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
@@ -85,6 +87,9 @@
std::vector<int32_t /* layerId */> mRemovedLayerHandles GUARDED_BY(mTraceLock);
std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
TransactionProtoParser mProtoParser GUARDED_BY(mTraceLock);
+ // Parses the transaction to proto without holding any tracing locks so we can generate proto
+ // in the binder thread without any contention.
+ TransactionProtoParser mLockfreeProtoParser;
// We do not want main thread to block so main thread will try to acquire mMainThreadLock,
// otherwise will push data to temporary container.
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/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 947fdce..cf44eff 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -204,8 +204,7 @@
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
mFlinger.mutableMaxRenderTargetSize() = 16384;
- flinger->setLayerTracingFlags(LayerTracing::TRACE_BUFFERS | LayerTracing::TRACE_INPUT |
- LayerTracing::TRACE_BUFFERS);
+ flinger->setLayerTracingFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
flinger->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos());
std::unique_ptr<TraceGenFlingerDataMapper> mapper =
std::make_unique<TraceGenFlingerDataMapper>();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 2fcf856..b796dfe 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -271,7 +271,7 @@
private:
// ICompositor overrides:
bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
- void composite(nsecs_t) override {}
+ void composite(nsecs_t, int64_t) override {}
void sample() override {}
};
}; // namespace scheduler
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/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index fcf4499..4f99b19 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -70,7 +70,7 @@
// Keep insync with layer_state_t
message LayerState {
- int32 layer_id = 1;
+ int64 layer_id = 1;
// Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
// and the next 32 bits are in ChangesMsb. This is needed because enums have to be
// 32 bits and there's no nice way to put 64bit constants into .proto files.
@@ -161,8 +161,8 @@
Matrix22 matrix = 11;
float corner_radius = 12;
uint32 background_blur_radius = 13;
- int32 parent_id = 14;
- int32 relative_parent_id = 15;
+ int64 parent_id = 14;
+ int64 relative_parent_id = 15;
float alpha = 16;
message Color3 {
@@ -233,7 +233,7 @@
bool focusable = 5;
bool has_wallpaper = 6;
float global_scale_factor = 7;
- int32 crop_layer_id = 8;
+ int64 crop_layer_id = 8;
bool replace_touchable_region_with_crop = 9;
RectProto touchable_region_crop = 10;
Transform transform = 11;
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..aa6c74e
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -0,0 +1,49 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "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/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 58cd7ba..1eea023 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -89,6 +89,8 @@
"LayerHistoryTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
+ "LayerTest.cpp",
+ "LayerTestUtils.cpp",
"MessageQueueTest.cpp",
"SurfaceFlinger_CreateDisplayTest.cpp",
"SurfaceFlinger_DestroyDisplayTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp
new file mode 100644
index 0000000..4974f90
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerTest.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <EffectLayer.h>
+#include <gtest/gtest.h>
+#include <ui/FloatRect.h>
+#include <ui/Transform.h>
+#include <limits>
+
+#include "LayerTestUtils.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+namespace {
+
+class LayerTest : public BaseLayerTest {
+protected:
+ static constexpr const float MIN_FLOAT = std::numeric_limits<float>::min();
+ static constexpr const float MAX_FLOAT = std::numeric_limits<float>::max();
+ static constexpr const FloatRect LARGE_FLOAT_RECT{MIN_FLOAT, MIN_FLOAT, MAX_FLOAT, MAX_FLOAT};
+};
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, LayerTest,
+ testing::Values(std::make_shared<BufferStateLayerFactory>(),
+ std::make_shared<EffectLayerFactory>()),
+ PrintToStringParamName);
+
+TEST_P(LayerTest, layerVisibleByDefault) {
+ sp<Layer> layer = GetParam()->createLayer(mFlinger);
+ layer->updateGeometry();
+ layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+ ASSERT_FALSE(layer->isHiddenByPolicy());
+}
+
+TEST_P(LayerTest, hideLayerWithZeroMatrix) {
+ sp<Layer> layer = GetParam()->createLayer(mFlinger);
+
+ layer_state_t::matrix22_t matrix{0, 0, 0, 0};
+ layer->setMatrix(matrix);
+ layer->updateGeometry();
+ layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+
+ ASSERT_TRUE(layer->isHiddenByPolicy());
+}
+
+TEST_P(LayerTest, hideLayerWithInfMatrix) {
+ sp<Layer> layer = GetParam()->createLayer(mFlinger);
+
+ constexpr const float INF = std::numeric_limits<float>::infinity();
+ layer_state_t::matrix22_t matrix{INF, 0, 0, INF};
+ layer->setMatrix(matrix);
+ layer->updateGeometry();
+ layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+
+ ASSERT_TRUE(layer->isHiddenByPolicy());
+}
+
+TEST_P(LayerTest, hideLayerWithNanMatrix) {
+ sp<Layer> layer = GetParam()->createLayer(mFlinger);
+
+ constexpr const float QUIET_NAN = std::numeric_limits<float>::quiet_NaN();
+ layer_state_t::matrix22_t matrix{QUIET_NAN, 0, 0, QUIET_NAN};
+ layer->setMatrix(matrix);
+ layer->updateGeometry();
+ layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+
+ ASSERT_TRUE(layer->isHiddenByPolicy());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
new file mode 100644
index 0000000..5a2c147
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#include "LayerTestUtils.h"
+
+#include "mock/MockEventThread.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+sp<Layer> BufferStateLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
+ LayerMetadata());
+ return new BufferStateLayer(args);
+}
+
+sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
+ return new EffectLayer(args);
+}
+
+std::string PrintToStringParamName(
+ const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
+ return info.param->name();
+}
+
+BaseLayerTest::BaseLayerTest() {
+ setupScheduler();
+}
+
+void BaseLayerTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread),
+ TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
+ TestableSurfaceFlinger::kTwoDisplayModes);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
new file mode 100644
index 0000000..fc9b6a2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <memory>
+
+#include <gtest/gtest.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+class LayerFactory {
+public:
+ virtual ~LayerFactory() = default;
+
+ virtual std::string name() = 0;
+ virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
+
+protected:
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+};
+
+class BufferStateLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "BufferStateLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override;
+};
+
+class EffectLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "EffectLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override;
+};
+
+std::string PrintToStringParamName(
+ const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info);
+
+class BaseLayerTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+protected:
+ BaseLayerTest();
+
+ void setupScheduler();
+
+ TestableSurfaceFlinger mFlinger;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 1dd7dea..e0aa0b1 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -33,7 +33,7 @@
struct NoOpCompositor final : ICompositor {
bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
- void composite(nsecs_t) override {}
+ void composite(nsecs_t, int64_t) override {}
void sample() override {}
} gNoOpCompositor;
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 2b69f13..825f145 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -30,73 +30,29 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
#include "FpsOps.h"
+#include "LayerTestUtils.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
#include "mock/MockVsyncController.h"
namespace android {
-using testing::_;
using testing::DoAll;
using testing::Mock;
-using testing::Return;
using testing::SetArgPointee;
using android::Hwc2::IComposer;
using android::Hwc2::IComposerClient;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
using scheduler::LayerHistory;
using FrameRate = Layer::FrameRate;
using FrameRateCompatibility = Layer::FrameRateCompatibility;
-class LayerFactory {
-public:
- virtual ~LayerFactory() = default;
-
- virtual std::string name() = 0;
- virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
-
-protected:
- static constexpr uint32_t WIDTH = 100;
- static constexpr uint32_t HEIGHT = 100;
- static constexpr uint32_t LAYER_FLAGS = 0;
-};
-
-class BufferStateLayerFactory : public LayerFactory {
-public:
- std::string name() override { return "BufferStateLayer"; }
- sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
- sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
- LayerMetadata());
- return new BufferStateLayer(args);
- }
-};
-
-class EffectLayerFactory : public LayerFactory {
-public:
- std::string name() override { return "EffectLayer"; }
- sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
- sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS,
- LayerMetadata());
- return new EffectLayer(args);
- }
-};
-
-std::string PrintToStringParamName(
- const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
- return info.param->name();
-}
-
/**
* This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
*/
-class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+class SetFrameRateTest : public BaseLayerTest {
protected:
const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default);
const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple);
@@ -106,14 +62,10 @@
SetFrameRateTest();
- void setupScheduler();
-
void addChild(sp<Layer> layer, sp<Layer> child);
void removeChild(sp<Layer> layer, sp<Layer> child);
void commitTransaction();
- TestableSurfaceFlinger mFlinger;
-
std::vector<sp<Layer>> mLayers;
};
@@ -122,8 +74,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- setupScheduler();
-
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
}
@@ -142,33 +92,6 @@
}
}
-void SetFrameRateTest::setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread),
- TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
- TestableSurfaceFlinger::kTwoDisplayModes);
-}
-
namespace {
TEST_P(SetFrameRateTest, SetAndGet) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 364d8f1..4708572 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -110,7 +110,7 @@
private:
// ICompositor overrides:
bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
- void composite(nsecs_t) override {}
+ void composite(nsecs_t, int64_t) override {}
void sample() override {}
};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index d83b9bb..fe0564e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -334,16 +334,14 @@
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
-
nsecs_t commit() {
- constexpr int64_t kVsyncId = 123;
const nsecs_t now = systemTime();
const nsecs_t expectedVsyncTime = now + 10'000'000;
mFlinger->commit(now, kVsyncId, expectedVsyncTime);
return now;
}
- void commitAndComposite() { mFlinger->composite(commit()); }
+ void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
auto createDisplay(const String8& displayName, bool secure) {
return mFlinger->createDisplay(displayName, secure);
@@ -877,6 +875,8 @@
};
private:
+ constexpr static int64_t kVsyncId = 123;
+
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger;
scheduler::mock::SchedulerCallback mSchedulerCallback;
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index eefa11f..ded7531 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -226,7 +226,7 @@
// if this is an animation, this thread should be blocked for 5s
// in setTransactionState waiting for transactionA to flush. Otherwise,
// the transaction should be placed on the pending queue
- if (flags & (ISurfaceComposer::eAnimation | ISurfaceComposer::eSynchronous) ||
+ if (flags & (ISurfaceComposer::eSynchronous) ||
syncInputWindows) {
EXPECT_GE(systemTime(),
applicationSentTime + mFlinger.getAnimationTransactionTimeout());
@@ -288,10 +288,6 @@
NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
}
-TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Animation) {
- NotPlacedOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
-}
-
TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
}
@@ -300,10 +296,6 @@
PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
}
-TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Animation) {
- PlaceOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
-}
-
TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
}
@@ -551,13 +543,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 +598,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/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index ab893a3..f5e3b77 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -81,7 +81,7 @@
sp<IBinder> getLayerHandle(int32_t id) const override {
return (id == 42) ? layerHandle : nullptr;
}
- int32_t getLayerId(const sp<IBinder>& handle) const override {
+ int64_t getLayerId(const sp<IBinder>& handle) const override {
return (handle == layerHandle) ? 42 : -1;
}
sp<IBinder> getDisplayHandle(int32_t id) const {
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;
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 9225062..60c0cb5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -632,8 +632,10 @@
switch (ext_bit) {
case ProcHook::KHR_android_surface:
case ProcHook::KHR_surface:
+ case ProcHook::KHR_surface_protected_capabilities:
case ProcHook::EXT_swapchain_colorspace:
case ProcHook::KHR_get_surface_capabilities2:
+ case ProcHook::GOOGLE_surfaceless_query:
hook_extensions_.set(ext_bit);
// return now as these extensions do not require HAL support
return;
@@ -710,8 +712,10 @@
case ProcHook::KHR_external_fence_capabilities:
case ProcHook::KHR_get_surface_capabilities2:
case ProcHook::KHR_surface:
+ case ProcHook::KHR_surface_protected_capabilities:
case ProcHook::EXT_debug_report:
case ProcHook::EXT_swapchain_colorspace:
+ case ProcHook::GOOGLE_surfaceless_query:
case ProcHook::ANDROID_native_buffer:
case ProcHook::EXTENSION_CORE_1_0:
case ProcHook::EXTENSION_CORE_1_1:
@@ -923,6 +927,9 @@
loader_extensions.push_back({
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_SURFACE_SPEC_VERSION});
+ loader_extensions.push_back(
+ {VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME,
+ VK_KHR_SURFACE_PROTECTED_CAPABILITIES_SPEC_VERSION});
loader_extensions.push_back({
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
VK_KHR_ANDROID_SURFACE_SPEC_VERSION});
@@ -932,6 +939,8 @@
loader_extensions.push_back({
VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION});
+ loader_extensions.push_back({VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME,
+ VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION});
static const VkExtensionProperties loader_debug_report_extension = {
VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION,
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 5f37a50..b436db1 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -565,11 +565,13 @@
if (strcmp(name, "VK_EXT_hdr_metadata") == 0) return ProcHook::EXT_hdr_metadata;
if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace;
if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
+ if (strcmp(name, "VK_GOOGLE_surfaceless_query") == 0) return ProcHook::GOOGLE_surfaceless_query;
if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2;
if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image;
if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
+ if (strcmp(name, "VK_KHR_surface_protected_capabilities") == 0) return ProcHook::KHR_surface_protected_capabilities;
if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 819f6b2..079f9cc 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -41,11 +41,13 @@
EXT_hdr_metadata,
EXT_swapchain_colorspace,
GOOGLE_display_timing,
+ GOOGLE_surfaceless_query,
KHR_android_surface,
KHR_get_surface_capabilities2,
KHR_incremental_present,
KHR_shared_presentable_image,
KHR_surface,
+ KHR_surface_protected_capabilities,
KHR_swapchain,
ANDROID_external_memory_android_hardware_buffer,
KHR_bind_memory2,
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 4a6b4f1..0be4403 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -619,42 +619,65 @@
VKAPI_ATTR
VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(
- VkPhysicalDevice /*pdev*/,
+ VkPhysicalDevice pdev,
VkSurfaceKHR surface,
VkSurfaceCapabilitiesKHR* capabilities) {
ATRACE_CALL();
int err;
- ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
-
int width, height;
- err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
- strerror(-err), err);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
- strerror(-err), err);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
-
int transform_hint;
- err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
- strerror(-err), err);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
-
int max_buffer_count;
- err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
- strerror(-err), err);
- return VK_ERROR_SURFACE_LOST_KHR;
+ if (surface == VK_NULL_HANDLE) {
+ const InstanceData& instance_data = GetData(pdev);
+ ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
+ bool surfaceless_enabled =
+ instance_data.hook_extensions.test(surfaceless);
+ if (!surfaceless_enabled) {
+ // It is an error to pass a surface==VK_NULL_HANDLE unless the
+ // VK_GOOGLE_surfaceless_query extension is enabled
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this
+ // extension for this function is for
+ // VkSurfaceProtectedCapabilitiesKHR::supportsProtected. The following
+ // four values cannot be known without a surface. Default values will
+ // be supplied anyway, but cannot be relied upon.
+ width = 1000;
+ height = 1000;
+ transform_hint = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ max_buffer_count = 10;
+ } else {
+ ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
+
+ err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+ &transform_hint);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+
+ err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT,
+ &max_buffer_count);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
}
capabilities->minImageCount = std::min(max_buffer_count, 3);
capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
@@ -695,23 +718,43 @@
const InstanceData& instance_data = GetData(pdev);
bool wide_color_support = false;
- Surface& surface = *SurfaceFromHandle(surface_handle);
- int err = native_window_get_wide_color_support(surface.window.get(),
- &wide_color_support);
- if (err) {
- return VK_ERROR_SURFACE_LOST_KHR;
- }
+ uint64_t consumer_usage = 0;
bool swapchain_ext =
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
- ALOGV("wide_color_support is: %d", wide_color_support);
+ if (surface_handle == VK_NULL_HANDLE) {
+ ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
+ bool surfaceless_enabled =
+ instance_data.hook_extensions.test(surfaceless);
+ if (!surfaceless_enabled) {
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ // Support for VK_GOOGLE_surfaceless_query. The EGL loader
+ // unconditionally supports wide color formats, even if they will cause
+ // a SurfaceFlinger fallback. Based on that, wide_color_support will be
+ // set to true in this case.
+ wide_color_support = true;
+
+ // TODO(b/203826952): research proper value; temporarily use the
+ // values seen on Pixel
+ consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;
+ } else {
+ Surface& surface = *SurfaceFromHandle(surface_handle);
+ int err = native_window_get_wide_color_support(surface.window.get(),
+ &wide_color_support);
+ if (err) {
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ ALOGV("wide_color_support is: %d", wide_color_support);
+
+ consumer_usage = surface.consumer_usage;
+ }
wide_color_support = wide_color_support && swapchain_ext;
AHardwareBuffer_Desc desc = {};
desc.width = 1;
desc.height = 1;
desc.layers = 1;
- desc.usage = surface.consumer_usage |
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+ desc.usage = consumer_usage | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
// We must support R8G8B8A8
@@ -731,6 +774,10 @@
VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
}
+ // NOTE: Any new formats that are added must be coordinated across different
+ // Android users. This includes the ANGLE team (a layered implementation of
+ // OpenGL-ES).
+
desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
if (AHardwareBuffer_isSupported(&desc)) {
all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -770,6 +817,10 @@
VK_COLOR_SPACE_PASS_THROUGH_EXT});
}
+ // NOTE: Any new formats that are added must be coordinated across different
+ // Android users. This includes the ANGLE team (a layered implementation of
+ // OpenGL-ES).
+
VkResult result = VK_SUCCESS;
if (formats) {
uint32_t transfer_count = all_formats.size();
@@ -814,6 +865,12 @@
.supportedUsageFlags;
} break;
+ case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
+ VkSurfaceProtectedCapabilitiesKHR* protected_caps =
+ reinterpret_cast<VkSurfaceProtectedCapabilitiesKHR*>(caps);
+ protected_caps->supportsProtected = VK_TRUE;
+ } break;
+
default:
// Ignore all other extension structs
break;
@@ -865,31 +922,51 @@
int err;
int query_value;
- ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
-
- err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &query_value);
- if (err != android::OK || query_value < 0) {
- ALOGE(
- "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
- "value=%d",
- strerror(-err), err, query_value);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
-
- err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
- if (err != android::OK || query_value < 0) {
- ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
- strerror(-err), err, query_value);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
-
std::vector<VkPresentModeKHR> present_modes;
- if (min_undequeued_buffers + 1 < max_buffer_count)
+ if (surface == VK_NULL_HANDLE) {
+ const InstanceData& instance_data = GetData(pdev);
+ ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
+ bool surfaceless_enabled =
+ instance_data.hook_extensions.test(surfaceless);
+ if (!surfaceless_enabled) {
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this
+ // extension for this function is for
+ // VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and
+ // VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR. We technically cannot
+ // know if VK_PRESENT_MODE_SHARED_MAILBOX_KHR is supported without a
+ // surface, and that cannot be relied upon.
present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
- present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+ present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+ } else {
+ ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
+
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &query_value);
+ if (err != android::OK || query_value < 0) {
+ ALOGE(
+ "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
+ "value=%d",
+ strerror(-err), err, query_value);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
+
+ err =
+ window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
+ if (err != android::OK || query_value < 0) {
+ ALOGE(
+ "NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
+ strerror(-err), err, query_value);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
+
+ if (min_undequeued_buffers + 1 < max_buffer_count)
+ present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
+ present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+ }
VkPhysicalDevicePresentationPropertiesANDROID present_properties;
QueryPresentationProperties(pdev, &present_properties);
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index 6a73023..af56764 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -27,11 +27,13 @@
'VK_EXT_hdr_metadata',
'VK_EXT_swapchain_colorspace',
'VK_GOOGLE_display_timing',
+ 'VK_GOOGLE_surfaceless_query',
'VK_KHR_android_surface',
'VK_KHR_get_surface_capabilities2',
'VK_KHR_incremental_present',
'VK_KHR_shared_presentable_image',
'VK_KHR_surface',
+ 'VK_KHR_surface_protected_capabilities',
'VK_KHR_swapchain',
]