diff options
163 files changed, 5445 insertions, 1931 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index e7bde213fb..77bed0e605 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 @@ done: 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 void chown_app_profile_dir(const std::string &packageName, int32_t appId, } } -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 @@ static binder::Status createAppDataDirs(const std::string& path, } // Prepare only the parent app directory - if (prepare_app_dir(path, targetMode, uid) || - prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || - prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { + 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 @@ binder::Status InstalldNativeService::createAppDataLocked( 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 @@ binder::Status InstalldNativeService::createAppDataLocked( 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 @@ binder::Status InstalldNativeService::createAppDataLocked( } } - // 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 @@ binder::Status InstalldNativeService::createAppDataBatched( 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 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri } } } + 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 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st } } } + 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 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } + // 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 @@ fail: } } } + 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 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s 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 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s 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 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string 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 @@ binder::Status InstalldNativeService::restoreconAppDataLocked( 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 @@ binder::Status InstalldNativeService::cleanupInvalidPackageDirs( 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 6b4ba1f72e..1f0fc9cf0f 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -58,12 +58,12 @@ public: 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 @@ private: 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 afedcc6adc..e08e9b6634 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -24,6 +24,8 @@ interface IInstalld { 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 @@ interface IInstalld { 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 0000000000..2f794b16cc --- /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 c796da6c46..9647865e4f 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -74,10 +74,19 @@ using android::base::ReadFdToString; 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 @@ static void open_profile_files(uid_t uid, const std::string& package_name, } } +// 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 @@ static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_EMPTY_PROFILES = 7 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 @@ class RunProfman : public ExecVHelper { 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 @@ class RunProfman : public ExecVHelper { 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 @@ class RunProfman : public ExecVHelper { 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 @@ static int analyze_profiles(uid_t uid, const std::string& package_name, 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 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& p 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 @@ bool copy_system_profile(const std::string& system_profile, _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 @@ static bool get_class_loader_context_dex_paths(const char* class_loader_context, } 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 @@ static SecondaryDexOptProcessResult process_secondary_dex_dexopt(const std::stri } /* 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 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins 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 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, _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 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg 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 @@ static bool create_app_profile_snapshot(int32_t app_id, } /* 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 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, } /* 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 @@ bool prepare_app_profile(const std::string& package_name, } 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 @@ bool prepare_app_profile(const std::string& package_name, } /* 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 e390babb57..b3baca5c41 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -26,6 +26,7 @@ cc_test { "libasync_safe", "libdiskusage", "libext2_uuid", + "libgmock", "libinstalld", "liblog", ], @@ -106,6 +107,7 @@ cc_test { "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 bb36c395e1..f21a304a3e 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -50,6 +50,8 @@ using android::base::unique_fd; 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 @@ class ProfileTest : public DexoptTest { _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 @@ class BootProfileTest : public ProfileTest { _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 703a09603f..04558d5b07 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 @@ std::string get_package_name(uid_t uid) { 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 @@ bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *ins 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 @@ static int create(const char* path, uid_t owner, gid_t group, mode_t mode) { 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 @@ static int stat_mode(const char* path) { 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 @@ static bool find_file(const char* path, Pred&& pred) { 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 @@ protected: 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); - - auto fd = create("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("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("==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); - - 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("b14b6458a44NOTdeleted/foo")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar")); - EXPECT_TRUE(exists("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("==deleted==/foo")); - EXPECT_TRUE(exists("==deleted==/foo/file")); - EXPECT_TRUE(exists("==deleted==/bar")); - EXPECT_TRUE(exists("==deleted==/bar/file")); - - EXPECT_TRUE(exists_renamed_deleted_dir()); - - service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE); - - 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_TRUE(exists("b14b6458a44NOTdeleted/foo")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar")); - EXPECT_TRUE(exists("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_FALSE(exists("==deleted==/foo")); - EXPECT_FALSE(exists("==deleted==/foo/file")); - EXPECT_FALSE(exists("==deleted==/bar")); - EXPECT_FALSE(exists("==deleted==/bar/file")); - - EXPECT_FALSE(exists_renamed_deleted_dir()); + 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(prefix + "/5b14b6458a44==deleted==/bar/opened_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(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(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(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(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(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(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("/" + prefix)); + + service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE); + + EXPECT_EQ(::close(fd), 0); + + 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(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(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(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(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 @@ TEST_F(ServiceTest, HashSecondaryDex_NoSuch) { 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_NoSuch) { 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_Unreadable) { 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 @@ TEST_F(ServiceTest, GetAppSize) { 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 @@ TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { "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 @@ protected: 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 @@ protected: 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"; - args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); // 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, + 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("misc_ce/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid, + CheckFileAccess(fooCePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid, 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, + 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("misc_de/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid, + CheckFileAccess(fooDePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid, S_IFDIR | S_ISGID | 0771); } -TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlag) { 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"); + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + + // Create the app user data. + 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_WithoutSdkFlagDeletesExisting) { + android::os::CreateAppDataResult result; + 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 a7a8624ff6..38c1c05c53 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 @@ TEST_F(UtilsTest, TestCreateDirIfNeeded) { ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700)); } -TEST_F(UtilsTest, TestSupplementalDataPaths) { - // 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)); +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_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_THAT(result, UnorderedElementsAre("com.foo", "com.bar")); +} - 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")); +TEST_F(UtilsTest, TestSdkSandboxDataPaths) { + // Ce data paths + 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/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/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/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/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", + 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/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/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 3ce4b67d9a..8cfd12313b 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 @@ std::string create_data_user_de_path(const char* volume_uuid, userid_t userid) { } /** - * 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 @@ static auto open_dir(const char* dir) { 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 @@ int ensure_config_user_dirs(userid_t userid) { 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 @@ void drop_capabilities(uid_t uid) { } } +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 2db1623cbe..54d77f95d6 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_de_package_path(const char* volume_uuid, 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 @@ int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_miss 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 validate_apk_path_subdirs(const char *path); 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 @@ bool collect_profiles(std::vector<std::string>* profiles_paths); 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 60d6492e70..8c000a11c9 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 @@ bool view_compiler(const char* apk_path, const char* package_name, const char* o _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 2837add22c..b23a9518f7 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -814,6 +814,8 @@ public: 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 7448308ec9..63d87dae5a 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -166,6 +166,7 @@ cc_library { "-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 0970ca5aa5..01b25d390d 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 @@ status_t BBinder::transact( 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 @@ status_t BBinder::onTransact( { 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 056ef0ab74..921e57c7bf 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -72,29 +72,29 @@ void* BpBinder::ObjectManager::attach(const void* objectID, void* object, void* 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 @@ void BpBinder::ObjectManager::kill() { 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 bd974b02b1..9c7ff97a48 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 @@ private: 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 @@ void HeapCache::binderDied(const wp<IBinder>& binder) 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 @@ sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder) 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 @@ void HeapCache::free_heap(const wp<IBinder>& binder) 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 @@ void HeapCache::free_heap(const wp<IBinder>& binder) 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> HeapCache::get_heap(const sp<IBinder>& binder) { 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 13f0a4c4a5..f79075d260 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -1199,7 +1199,8 @@ status_t IPCThreadState::executeCommand(int32_t cmd) 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 @@ status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received 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 f84f639d2c..504c6c2d83 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 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { } flat_binder_object obj; - obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; int schedBits = 0; if (!IPCThreadState::self()->backgroundSchedulingDisabled()) { @@ -221,6 +220,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { 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 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { // 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 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { } } 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 1821729cfa..b14a838447 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 @@ void ProcessState::expungeHandle(int32_t handle, IBinder* binder) 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 @@ size_t ProcessState::getThreadPoolMaxThreadCount() const { 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 @@ static base::Result<int> open_driver(const char* driver) { 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 b84395e7cb..e79cb86ffa 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -152,8 +152,13 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) { } 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 4ddbce71a8..2e7084e12e 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -125,8 +125,8 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind 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 c0454b6fbd..8deb2fe4a8 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 @@ public: 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 ea40db8ffa..bb55831ec2 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -56,8 +56,16 @@ public: 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 cf8d8e433c..0deee73e43 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -91,6 +91,12 @@ public: */ 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 2c471c6e8f..7ea9be797b 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 @@ class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nul 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 09411e76a3..b3bc7f449e 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 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, // 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 972eca79ff..28819bb42a 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 @@ class AParcelableHolder { 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 @@ class AParcelableHolder { 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 @@ class AParcelableHolder { } 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 @@ class AParcelableHolder { 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 @@ class AParcelableHolder { 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 @@ class AParcelableHolder { 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 @@ class AParcelableHolder { } 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 2a66941cef..dfa8ea28e7 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 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const /** * 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 7c5afde85c..c9d6af0d75 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -924,9 +924,9 @@ mod tests { 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 700940a529..b1e17b7892 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -57,6 +57,7 @@ using namespace std::chrono_literals; 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 @@ public: 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 @@ TEST_F(BinderLibRpcTest, SetRpcClientDebugTwice) { 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 @@ TEST_P(BinderLibRpcTestP, SetRpcClientDebugNoFd) { 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 @@ TEST_P(BinderLibRpcTestP, SetRpcClientDebugNoKeepAliveBinder) { 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 1513ecafc8..45a6d47bb4 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 @@ TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { 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 @@ TEST_F(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) { } } +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 7ce72ffa59..ec4c7c10a4 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -132,12 +132,14 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } -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 @@ BLASTBufferQueue::~BLASTBufferQueue() { 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 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, 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 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, // 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 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, // 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 @@ void BLASTBufferQueue::acquireNextBufferLocked( // 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 @@ void BLASTBufferQueue::acquireNextBufferLocked( 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 @@ void BLASTBufferQueue::acquireNextBufferLocked( 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 @@ void BLASTBufferQueue::acquireNextBufferLocked( 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::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& l } 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)); + + 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; + } + } - 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 (mayNeedToWaitForBuffer) { + flushAndWaitForFreeBuffer(_lock); } } - if (mayNeedToWaitForBuffer) { - flushAndWaitForFreeBuffer(_lock); + // add to shadow queue + mNumFrameAvailable++; + if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) { + acquireAndReleaseBuffer(); } - } - - // 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; + 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); } - mWaitForTransactionCallback = true; - } else if (!mWaitForTransactionCallback) { - acquireNextBufferLocked(std::nullopt); + } + if (prevCallback) { + prevCallback(prevTransaction); } } @@ -679,12 +698,37 @@ void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { 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 @@ void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) { 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 @@ void BLASTBufferQueue::abandon() { 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 75c5e26fb0..5532c6e747 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -46,9 +46,11 @@ using namespace aidl::android::hardware::graphics; 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 @@ public: 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 @@ public: 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 @@ status_t BnSurfaceComposer::onTransact( 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 9022e7d5a4..338ff1114f 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -686,85 +686,89 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrame // ---------------------------------------------------------------------------- -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 @@ status_t BufferData::writeToParcel(Parcel* output) const { 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 @@ status_t BufferData::readFromParcel(const Parcel* input) { 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 9269c3e5a7..27856cef13 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 @@ using ui::ColorMode; // --------------------------------------------------------------------------- ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService); +ANDROID_SINGLETON_STATIC_INSTANCE(ComposerServiceAIDL); namespace { // Initialize transaction id counter used to generate transaction ids @@ -120,6 +122,52 @@ void ComposerService::composerServiceDied() 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 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } } -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 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { 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 @@ std::shared_ptr<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffe 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 SurfaceComposerClient::removeWindowInfosListener( 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 80bd6389a0..2312a8cf0d 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -51,11 +51,11 @@ bool WindowInfo::supportsSplitTouch() const { } 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 @@ bool WindowInfo::operator==(const WindowInfo& info) const { 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 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { 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 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { 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 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { 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 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { 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 0000000000..2caa2b9f61 --- /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 0000000000..07921a59a5 --- /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 0000000000..f0def5019a --- /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 90186351c5..b13c60049c 100644 --- a/libs/gui/android/gui/FocusRequest.aidl +++ b/libs/gui/android/gui/FocusRequest.aidl @@ -21,7 +21,7 @@ parcelable FocusRequest { /** * 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 1ed592b506..65fc04df1a 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -73,7 +73,7 @@ class BLASTBufferQueue : 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 @@ public: 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 @@ private: 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 @@ private: // 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 0000000000..ec884cfa8c --- /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 0a59f52fcd..0a3cc19a13 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -58,11 +58,9 @@ namespace android { 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::IRegionSamplingListener; using gui::IScreenCaptureListener; using gui::SpHash; +namespace gui { + +struct DisplayCaptureArgs; +struct LayerCaptureArgs; + +} // namespace gui + namespace ui { struct DisplayMode; @@ -108,6 +113,7 @@ public: // android.permission.ACCESS_SURFACE_FLINGER eEarlyWakeupStart = 0x08, eEarlyWakeupEnd = 0x10, + eOneWay = 0x20 }; enum VsyncSource { @@ -261,27 +267,6 @@ public: */ 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 @@ public: 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 @@ public: 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 0000000000..05ff9d5b7b --- /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 f7206193cd..0f37dab53c 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 @@ public: // 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 @@ struct layer_state_t { // 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 @@ static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) 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 25637efce2..c8ac1662ad 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -57,7 +57,9 @@ class IGraphicBufferProducer; 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 @@ public: // 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 @@ public: 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 b9bffaa6a0..ef0b98b5cb 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 @@ struct WindowInfo : public Parcelable { ftl_last = FIRST_SYSTEM_WINDOW + 15 }; - // This is a conversion of os::IInputConstants::InputFeature to an enum backed by an unsigned + // Flags used to determine configuration of this input window. + // 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 Feature : uint32_t { + enum class InputConfig : uint32_t { // clang-format off + DEFAULT = + static_cast<uint32_t>(os::InputConfig::DEFAULT), NO_INPUT_CHANNEL = - static_cast<uint32_t>(os::IInputConstants::InputFeature::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::IInputConstants::InputFeature::DISABLE_USER_ACTIVITY), + static_cast<uint32_t>(os::InputConfig::DISABLE_USER_ACTIVITY), DROP_INPUT = - static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT), + static_cast<uint32_t>(os::InputConfig::DROP_INPUT), DROP_INPUT_IF_OBSCURED = - static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT_IF_OBSCURED), + static_cast<uint32_t>(os::InputConfig::DROP_INPUT_IF_OBSCURED), SPY = - static_cast<uint32_t>(os::IInputConstants::InputFeature::SPY), + static_cast<uint32_t>(os::InputConfig::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. - 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, + static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS), // clang-format on }; @@ -228,7 +224,6 @@ struct WindowInfo : public Parcelable { 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 fa1071a4e3..05ed0a0576 100644 --- a/libs/gui/include/private/gui/ComposerService.h +++ b/libs/gui/include/private/gui/ComposerService.h @@ -37,7 +37,7 @@ class ISurfaceComposer; // 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 0000000000..fee37eefe0 --- /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 42a32f3b42..0c3236c9ce 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 @@ public: 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 @@ protected: 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 @@ TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { 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 @@ TEST_F(BLASTBufferQueueTest, Update) { 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 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { 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 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) { 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 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { 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 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { 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 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { 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 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { 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 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { 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 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { 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 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { 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 @@ TEST_F(BLASTBufferQueueTest, DISABLED_DisconnectProducerTest) { 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 @@ TEST_F(BLASTBufferQueueTest, DISABLED_UpdateSurfaceControlTest) { 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 605628096d..a885e926a3 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 @@ protected: 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 @@ public: 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 ff9bae2800..c51b244c50 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -64,7 +64,6 @@ TEST(WindowInfo, Parcelling) { 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 @@ TEST(WindowInfo, Parcelling) { 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 930d8194d5..606fe2a59d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -30,6 +30,7 @@ filegroup { "android/os/IInputConstants.aidl", "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", + "android/os/InputConfig.aidl", ], } @@ -79,11 +80,8 @@ cc_library { 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 @@ cc_library { "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 3073d94dbe..fe1754c78b 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -846,6 +846,7 @@ vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& t 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 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, 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 265cbf0c0b..5ce10a4a50 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -47,55 +47,6 @@ interface IInputConstants */ 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 0000000000..6d1b3967f7 --- /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 a3eebdd5ee..8240b08085 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -111,6 +111,7 @@ struct ChoreographerFrameCallbackDataImpl { 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 @@ private: void scheduleCallbacks(); ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const; + void registerStartTime() const; std::mutex mLock; // Protected by mLock @@ -172,6 +174,9 @@ private: 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 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t if (cb.vsyncCallback != nullptr) { const ChoreographerFrameCallbackDataImpl frameCallbackData = createFrameCallbackData(timestamp); + registerStartTime(); mInCallback = true; cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>( &frameCallbackData), @@ -452,6 +458,16 @@ ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_ .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 @@ int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) { 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 19be5bd4eb..cdf7e66ecb 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_routeGetFrameTimelineExpectedPresentatio 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 2da10cdc46..969d9379f0 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -42,6 +42,7 @@ LIBNATIVEDISPLAY_PLATFORM { 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 07c5dd8a82..84e84dddad 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -122,6 +122,9 @@ cc_library_static { ":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 a426850350..d91af1ea85 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -42,6 +42,9 @@ cc_test { "libshaders", "libtonemap", ], + header_libs: [ + "libtonemap_headers", + ], shared_libs: [ "libbase", diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp index 390b22821e..1cd143e98d 100644 --- a/libs/shaders/Android.bp +++ b/libs/shaders/Android.bp @@ -38,6 +38,10 @@ cc_library_static { "libui-types", ], + header_libs: [ + "libtonemap_headers", + ], + srcs: [ "shaders.cpp", ], diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp index 5360fe2b07..99d1b22717 100644 --- a/libs/tonemap/Android.bp +++ b/libs/tonemap/Android.bp @@ -25,7 +25,6 @@ cc_library_static { name: "libtonemap", vendor_available: true, - export_include_dirs: ["include"], local_include_dirs: ["include"], shared_libs: [ @@ -41,3 +40,9 @@ cc_library_static { "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 f46f3fa27d..d519482fe3 100644 --- a/libs/tonemap/tests/Android.bp +++ b/libs/tonemap/tests/Android.bp @@ -27,6 +27,9 @@ cc_test { 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 b34d90699f..42dd85e959 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -134,6 +134,10 @@ float Transform::dsdy() const { 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 @@ Transform Transform::inverse() const { 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 33fbe05035..f1178cab35 100644 --- a/libs/ui/include/ui/Transform.h +++ b/libs/ui/include/ui/Transform.h @@ -77,6 +77,7 @@ public: 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 a9bd4bb2e2..a47fb9a573 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 67ce9f281e..974ae38b13 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 @@ inline int32_t bitcast_int32<uint32_t>(uint32_t source) { 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 @@ GpuWork::~GpuWork() { { 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 @@ void GpuWork::initialize() { 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 @@ void GpuWork::initialize() { { 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::initialize() { 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 @@ void GpuWork::dump(const Vector<String16>& /* args */, std::string* result) { // 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 {}; - }); - } - - // 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; - } - } + mGpuWorkMap.iterateWithValue( + [&dumpMap](const GpuIdUid& key, const UidTrackingInfo& value, + const android::bpf::BpfMap<GpuIdUid, UidTrackingInfo>&) + -> base::Result<void> { + dumpMap[key] = value; + return {}; + }); } - // 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 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullAtomCallback(int32_t atomTag, 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 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullFrequencyAtoms(AStatsEventList 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()); - - 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)); + .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; + } + + // 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 @@ void GpuWork::clearMapIfNeeded() { 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 @@ void GpuWork::clearMap() { // 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 a0e1b22540..c8e79a2824 100644 --- a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c +++ b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c @@ -27,65 +27,123 @@ #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. +// +// |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|. +// +// 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: // -// 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. +// - 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. // -// 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. +// - 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. - uint32_t uid; + // A value that uniquely identifies the GPU within the system. + uint32_t gpu_id; - // 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 UID of the application (i.e. persistent, unique ID of the Android + // app) that submitted work to the GPU. + uint32_t uid; // The start time of the period in nanoseconds. The clock must be // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function. @@ -95,54 +153,44 @@ typedef struct { // 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 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; - -} 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 " + // 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; + +} GpuWorkPeriodEvent; + +_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; + + 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| for |uid|. - UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&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 @@ DEFINE_BPF_PROG("tracepoint/power/gpu_work_period", AID_ROOT, AID_GRAPHICS, tp_g __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; - } + // |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 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 time in state. - __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[frequency_index], - args->end_time_ns - args->start_time_ns); + // Update |previous_active_end_time_ns|. + uid_tracking_info->previous_active_end_time_ns = period->end_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. + // 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; + } + } + + 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 { - // 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; - - // Update |previous_end_time_ns|. - uid_tracking_info->previous_end_time_ns = args->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); - } + 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 0; + 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 4fe84649a0..57338f4c91 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 android { 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 total amount of time the GPU has spent running work for the UID, in + // nanoseconds. + uint64_t total_active_duration_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 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 times we received |GpuUidWorkPeriodEvent| events in an - // unexpected order. See |GpuUidWorkPeriodEvent|. + // 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 @@ typedef struct { 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 b6f493d557..acecd569aa 100644 --- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h +++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h @@ -41,7 +41,7 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: 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 02ca07f968..34dc2eac06 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 4a77d9ab92..9fa901675d 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 @@ public class GpuWorkTracepointTest extends BaseHostJUnit4Test { "\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 cd20a646a7..9691ad8a79 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -61,18 +61,13 @@ private: 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 58c93036bb..06ad6a8f61 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -66,35 +66,77 @@ namespace android::inputdispatcher { 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 @@ const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::mil // 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 @@ bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) 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 @@ bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { // --- 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 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mMaximumObscuringOpacityForTouch(1.0f), mFocusedDisplayId(ADISPLAY_ID_DEFAULT), mWindowTokenWithPointerCapture(nullptr), + mStaleEventTimeout(staleEventTimeout), mLatencyAggregator(), mLatencyTracker(&mLatencyAggregator) { mLooper = new Looper(false); @@ -901,6 +941,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } +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 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo 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 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { 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 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( 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 @@ sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId 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 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( 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 @@ void InputDispatcher::setInputWindowsLocked( 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 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& } } -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) { - scoped_unlock unlock(mLock); - mPolicy->notifyWindowUnresponsive(token, reason); - }; - postCommandLocked(std::move(command)); -} - -void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) { - auto command = [this, pid]() REQUIRES(mLock) { + auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) { scoped_unlock unlock(mLock); - mPolicy->notifyMonitorResponsive(pid); + mPolicy->notifyWindowUnresponsive(token, pid, reason); }; postCommandLocked(std::move(command)); } -void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) { - auto command = [this, connectionToken]() 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->notifyWindowResponsive(connectionToken); + mPolicy->notifyWindowResponsive(token, pid); }; postCommandLocked(std::move(command)); } @@ -5857,22 +5873,21 @@ void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& conne 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::processConnectionUnresponsiveLocked(const Connection& conn */ 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 @@ void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& window 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 e162c78b81..3c79c988c4 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -84,6 +84,8 @@ public: 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 @@ private: */ 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 @@ private: */ 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 3c1e6370b7..de0b6da884 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -51,30 +51,19 @@ public: 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 ffce9f68cc..91be4a30bf 100644 --- a/services/inputflinger/tests/FocusResolver_test.cpp +++ b/services/inputflinger/tests/FocusResolver_test.cpp @@ -84,6 +84,30 @@ TEST(FocusResolverTest, SetFocusedWindow) { 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 ae8358aa5d..9633932e75 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -53,6 +53,11 @@ static const int32_t DEVICE_ID = 1; 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 @@ static const int32_t INJECTOR_UID = 1001; // 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 @@ static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) { class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { InputDispatcherConfiguration mConfig; + using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>; + protected: virtual ~FakeInputDispatcherPolicy() {} @@ -161,122 +170,71 @@ public: 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); - } - - void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) { - sp<IBinder> connectionToken = getResponsiveWindowToken(); - 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 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); - } - - sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) { + void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, + const sp<IBinder>& expectedToken, + int32_t expectedPid) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); - return getAnrTokenLockedInterruptible(timeout, mAnrWindowTokens, lock); + AnrResult result; + ASSERT_NO_FATAL_FAILURE(result = + getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock)); + const auto& [token, pid] = result; + ASSERT_EQ(expectedToken, token); + ASSERT_EQ(expectedPid, pid); } - sp<IBinder> getResponsiveWindowToken() { + /** 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(0s, mResponsiveWindowTokens, lock); + AnrResult result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock); + const auto& [token, _] = result; + return token; } - int32_t getUnresponsiveMonitorPid(std::chrono::nanoseconds timeout) { + void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken, + int32_t expectedPid) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); - return getAnrTokenLockedInterruptible(timeout, mAnrMonitorPids, lock); + AnrResult result; + ASSERT_NO_FATAL_FAILURE( + result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock)); + const auto& [token, pid] = result; + ASSERT_EQ(expectedToken, token); + ASSERT_EQ(expectedPid, pid); } - int32_t getResponsiveMonitorPid() { + /** 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, 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 @@ private: // 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 @@ private: sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; - void notifyConfigurationChanged(nsecs_t when) override { - std::scoped_lock lock(mLock); - mConfigurationChangedTime = when; + // 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; } - void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, const std::string&) override { - std::scoped_lock lock(mLock); - mAnrWindowTokens.push(connectionToken); - mNotifyAnr.notify_all(); + 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 notifyMonitorUnresponsive(int32_t pid, const std::string&) override { + void notifyConfigurationChanged(nsecs_t when) override { std::scoped_lock lock(mLock); - mAnrMonitorPids.push(pid); - mNotifyAnr.notify_all(); + mConfigurationChangedTime = when; } - void notifyWindowResponsive(const sp<IBinder>& connectionToken) override { + void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid, + const std::string&) override { std::scoped_lock lock(mLock); - mResponsiveWindowTokens.push(connectionToken); + ASSERT_TRUE(pid.has_value()); + mAnrWindows.push({connectionToken, *pid}); mNotifyAnr.notify_all(); } - void notifyMonitorResponsive(int32_t pid) override { + void notifyWindowResponsive(const sp<IBinder>& connectionToken, + std::optional<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 @@ protected: 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 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // 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 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: mInfo.ownerUid = ownerUid; } + int32_t getPid() const { return mInfo.ownerPid; } + void destroyReceiver() { mInputReceiver = nullptr; } int getChannelFd() { return mInputReceiver->getChannelFd(); } @@ -1503,6 +1517,10 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 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 @@ TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { // 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 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { // 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 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { 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 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { // 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 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { // 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 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { // 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 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { // 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 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { // 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 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { // 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 @@ TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) { // 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 @@ TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) { // 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 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform 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 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTrans 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 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTrans 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 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc 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 @@ protected: 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 @@ TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) // 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 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowAnr) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnKey) // 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 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnKey) 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 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnMoti 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 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnMoti 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 @@ TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) { 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 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { 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 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { mFocusedWindow->assertNoEvents(); mUnfocusedWindow->assertNoEvents(); ASSERT_TRUE(mDispatcher->waitForIdle()); - mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(), + mFocusedWindow->getPid()); mFakePolicy->assertNotifyAnrWasNotCalled(); } @@ -4817,8 +4893,9 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout 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 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout 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 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { 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 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { // 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 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { 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 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { 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 @@ class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest { "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 @@ TEST_F(InputDispatcherMultiWindowOcclusionTests, "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 @@ TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { 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 @@ TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { 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 @@ TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) { 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 @@ TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) { 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 @@ TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) { 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 @@ public: 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 @@ TEST_F(InputDispatcherSpyWindowTest, ContinuesToReceiveGestureAfterPilfer) { // 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 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesMultiplePointers) { 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 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { 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 @@ TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) { // 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 @@ public: 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, ConsmesOnlyStylusEvents) { 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 df4db199f8..455a1e2133 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -42,4 +42,7 @@ cc_fuzz { srcs: [ "LatencyTrackerFuzzer.cpp", ], + fuzz_config: { + cc: ["android-framework-input@google.com"], + }, } diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp index 86c8b0dbea..4d1de96caa 100644 --- a/services/sensorservice/AidlSensorHalWrapper.cpp +++ b/services/sensorservice/AidlSensorHalWrapper.cpp @@ -232,7 +232,7 @@ void convertToSensorEvent(const Event &src, sensors_event_t *dst) { 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 d9958f31c5..000a2cb0d3 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -76,6 +76,7 @@ cc_defaults { "libaidlcommonsupport", "libcompositionengine", "libframetimeline", + "libgui_aidl_static", "libperfetto_client_experimental", "librenderengine", "libscheduler", diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 18a6baeffe..0aca24ae70 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -311,6 +311,7 @@ void BufferLayer::preparePerFrameCompositionState() { ? 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 02e444df8e..e6a76e8ea9 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -271,7 +271,8 @@ static bool assignTransform(ui::Transform* dst, ui::Transform& from) { // 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 669eaadfec..8a696f11b4 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -141,6 +141,8 @@ private: 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 aefc014062..9302b7bc37 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -41,6 +41,10 @@ cc_defaults { "libtonemap", "libtrace_proto", "libaidlcommonsupport", + "libprocessgroup", + "libcgrouprc", + "libjsoncpp", + "libcgrouprc_format", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", @@ -68,6 +72,7 @@ cc_library { "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 47aacc99f6..6a3fcb7307 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -57,7 +57,7 @@ public: 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 c553fce85d..ca86f4c604 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h @@ -72,6 +72,9 @@ public: 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 8bf7f8fbfa..283fe86f43 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -166,6 +166,7 @@ struct LayerFECompositionState { 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 d8644a428d..15551029b3 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 @@ struct LayerFECompositionState; namespace impl { struct OutputCompositionState; +struct GpuCompositionResult; } // namespace impl /** @@ -262,6 +264,9 @@ public: // 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 @@ protected: 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 @@ protected: 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 daee83bd2c..9ee779cca1 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h @@ -100,6 +100,9 @@ public: // 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 c7984bd529..3b8b06fc2f 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 @@ public: 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 @@ public: 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 @@ private: 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 0000000000..ed1ddc172c --- /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 0000000000..11c0054089 --- /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 a7a8e97be5..0be5d018f6 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 @@ public: 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 @@ protected: 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 @@ private: 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 66dd825e5b..92f22b6a16 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 @@ struct OutputCompositionState { // 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 a8a538003e..e4cb113645 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h @@ -63,6 +63,7 @@ public: 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 14324de7ce..cb00e719f8 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: &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 84afb59510..72e6f3bdbb 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -34,13 +34,14 @@ public: 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 b68b95d07b..27303a82be 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 @@ public: 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 @@ public: 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 fe858c2817..e12aebb50c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h @@ -45,6 +45,7 @@ public: 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 6390025725..09648c356d 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -66,7 +66,7 @@ void Display::setConfiguration(const compositionengine::DisplayCreationArgs& arg 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 @@ std::optional<DisplayId> Display::getDisplayId() const { return mId; } -int32_t Display::getPreferredBootModeId() const { - return mPreferredBootDisplayModeId; +int32_t Display::getPreferredBootHwcConfigId() const { + return mPreferredBootHwcConfigId; } void Display::disconnect() { @@ -221,12 +221,12 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& 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 @@ void Display::chooseCompositionStrategy() { // 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 @@ void Display::chooseCompositionStrategy() { 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 @@ bool Display::getSkipColorTransform() const { 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::setExpensiveRenderingExpected(bool enabled) { } } -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 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre 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 db6d4f2fed..28900af162 100644 --- a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp @@ -20,4 +20,8 @@ namespace android::compositionengine { 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 0000000000..497424a327 --- /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 ff332ebe2c..cd10bc10b5 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 @@ void Output::present(const compositionengine::CompositionRefreshArgs& refreshArg 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 @@ void Output::prepareFrame() { 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 @@ void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& 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 @@ void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& prepareFrame(); } -void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) { +void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) { ATRACE_CALL(); ALOGV(__FUNCTION__); @@ -993,9 +1049,25 @@ void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refres 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 @@ void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refres 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 @@ std::optional<base::unique_fd> Output::composeSurfaces( } 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 @@ std::optional<base::unique_fd> Output::composeSurfaces( 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 @@ std::optional<base::unique_fd> Output::composeSurfaces( 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 @@ std::optional<base::unique_fd> Output::composeSurfaces( // 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 @@ void Output::dirtyEntireOutput() { 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 @@ compositionengine::Output::FrameFences Output::presentAndGetFrameFences() { 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 3e983f3b20..723593d7ac 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -325,7 +325,8 @@ void OutputLayer::updateCompositionState( // 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 @@ void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) 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 @@ std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() co }}; 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 12c2c8eb38..5a3af7bfea 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -289,5 +289,9 @@ std::shared_ptr<renderengine::ExternalTexture>& RenderSurface::mutableTextureFor 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 cd0323555c..36b04d909f 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 @@ struct PartialMockDisplayTestCommon : public DisplayTestCommon { 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 @@ struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon { 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 @@ struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon { StrictMock<HWC2::mock::Layer> hwc2LayerUnknown; std::shared_ptr<Display> mDisplay = createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay()); + impl::GpuCompositionResult mResultWithBuffer; + impl::GpuCompositionResult mResultWithoutBuffer; }; /* @@ -554,7 +579,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfGpuDisplay) { 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 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) { 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 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { 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 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithDisplayBrightnes 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, normalOperationWithDisplayBrightnes } 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 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { 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 @@ TEST_F(DisplayFinishFrameTest, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) { 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 @@ TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) { 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 @@ TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) { 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 dda0822b1f..8eb1946b67 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -657,7 +657,7 @@ TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly 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 @@ TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) { 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 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { 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 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { mLayerFEState.acquireFence = kFence; mOutputState.displayBrightnessNits = kDisplayBrightnessNits; + mOutputState.sdrWhitePointNits = kSdrWhitePointNits; EXPECT_CALL(mOutput, getDisplayColorProfile()) .WillRepeatedly(Return(&mDisplayColorProfile)); @@ -1117,7 +1122,7 @@ TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) { 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 @@ TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSe 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 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) { 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 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresen 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 c2521b2fc2..af172d8997 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 OutputPrepareFrameTest : 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, void()); + MOCK_METHOD0(chooseCompositionStrategy, std::optional<DeviceRequestedChanges>()); }; OutputPrepareFrameTest() { @@ -1037,6 +1038,133 @@ TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) { 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 @@ struct OutputPresentTest : public testing::Test { 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 @@ TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) { 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 @@ struct OutputDevOptRepaintFlashTest : public testing::Test { // 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 @@ TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty 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 OutputFinishFrameTest : public testing::Test { 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 @@ struct OutputFinishFrameTest : public testing::Test { 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 OutputComposeSurfacesTest : public testing::Test { 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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) 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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotS 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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLa 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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { .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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw 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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRende 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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRend 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 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRend 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 @@ struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSu 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 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDatas .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 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpen 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 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotEx 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 0b1c2626a1..bd4ff13236 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 @@ TEST_F(LayerStateTest, updateBuffer) { 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 eef00522dc..610d86f29a 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -91,6 +91,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) static_cast<uint32_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers)); } + mCompositionDisplay->setPredictCompositionStrategy(mFlinger->mPredictCompositionStrategy); mCompositionDisplay->createDisplayColorProfile( compositionengine::DisplayColorProfileCreationArgsBuilder() .setHasWideColorGamut(args.hasWideColorGamut) @@ -235,6 +236,15 @@ DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const { 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 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { } 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 bf6b31a76b..690f2404fd 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -232,6 +232,9 @@ public: // 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 5b2e265eb6..a8d439b7e0 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -269,6 +269,14 @@ public: 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 6f02843f1a..3dab38975c 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 @@ int32_t getUpdateTimeout() { 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 @@ public: 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 @@ public: } 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 307da41667..e21095aa88 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -89,6 +89,9 @@ public: 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 808109603f..aeaf1e1a14 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -338,6 +338,12 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform, // 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 @@ bool Layer::isHiddenByPolicy() const { } } } + 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 @@ ui::Transform Layer::getTransform() const { 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 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet (*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 @@ gui::DropInputMode Layer::getDropInputMode() const { } 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 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { // 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 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { // 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 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { 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 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { // 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 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp 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 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp // 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 21dd5f4954..2b5e337095 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -752,6 +752,7 @@ public: 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 43a6e55a09..2ece51c3dc 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -3,4 +3,5 @@ alecmouri@google.com 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 712cd5bdf3..f2af85e94a 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -52,7 +52,7 @@ void MessageQueue::Handler::handleMessage(const Message&) { 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 9532e26a9c..4082e26874 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -35,7 +35,7 @@ namespace android { 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 9c32b1f421..f6c81c0ba2 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -169,6 +169,7 @@ public: // 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 2cd34e9cb8..efaa975ff2 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -480,6 +480,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI 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 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI 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 @@ LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { 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::getBootDisplayModeSupport(bool* outSupport) const { 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::onComposerHalRefresh(hal::HWDisplayId) { } 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 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected 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 @@ void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats, 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 @@ void SurfaceFlinger::postComposition() { 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 @@ FloatRect SurfaceFlinger::getMaxDisplayBounds() { // 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 @@ bool SurfaceFlinger::stopTransactionProcessing( 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); - } - - 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. + 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); + + // 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 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { 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 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s 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 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( 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 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( 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 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( 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 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { 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 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { } 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 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { } 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 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { 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 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { 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 @@ bool SurfaceFlinger::commitCreatedLayers() { 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 3ecce33d33..86c83338ff 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 RegionSamplingThread; 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 @@ public: 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 @@ private: 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 @@ private: // 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 @@ private: // 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 @@ private: 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 @@ private: 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 @@ private: // 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 @@ private: } 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 16f6e31946..20fa091730 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -371,5 +371,9 @@ bool enable_layer_caching(bool defaultValue) { 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 8d0e4263b1..080feee686 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -100,6 +100,8 @@ bool enable_layer_caching(bool defaultValue); 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 0000000000..20f2aa8b06 --- /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 1e5c3e70c8..d249b60f2a 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -180,13 +180,13 @@ proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) { } 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 @@ void TransactionProtoParser::mergeFromProto(const proto::LayerState& proto, 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::mergeFromProto(const proto::LayerState& proto, } 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 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta 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 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta } 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 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta 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 2f70b27bf6..872a901b21 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.h +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h @@ -80,7 +80,8 @@ public: 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 @@ public: 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 @@ private: 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 d5e837f879..6381758c7b 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 @@ public: } }; +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::dump(std::string& result) const { } 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 @@ void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& comm 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 @@ void TransactionTracing::updateStartingStateLocked( // 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 95256c4047..4c291f960f 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 @@ private: 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 @@ private: 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 114f1ebb2d..e8fe734a8f 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 947fdcee4e..cf44effe50 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -204,8 +204,7 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, 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 2fcf8564fe..b796dfeb1f 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -271,7 +271,7 @@ public: 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 2e9e659880..3598308338 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -136,6 +136,8 @@ message LayerProto { // 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 fcf4499dcb..4f99b192a3 100644 --- a/services/surfaceflinger/layerproto/transactions.proto +++ b/services/surfaceflinger/layerproto/transactions.proto @@ -70,7 +70,7 @@ message TransactionState { // 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 @@ message LayerState { 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 @@ message LayerState { 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 caeff4ab21..ec180548e1 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -152,6 +152,11 @@ int main(int, char**) { 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 7702ea240b..bcbe21a483 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -461,4 +461,13 @@ prop { 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 bf1e7e2908..348a462038 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -69,6 +69,10 @@ props { 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 fa1a5ed6b0..094b0ffc0f 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 @@ TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { 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 @@ TEST(LayerStateTest, ParcellingLayerCaptureArgs) { 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 0000000000..aa6c74eb65 --- /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 0000000000..c0aeb355a7 --- /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 0000000000..ac4354cde1 --- /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 0000000000..3e80a741c4 --- /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 Binary files differnew file mode 100644 index 0000000000..9e4005c2cf --- /dev/null +++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope Binary files differnew file mode 100644 index 0000000000..8356ae7992 --- /dev/null +++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 58cd7ba96a..1eea023f17 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -89,6 +89,8 @@ cc_test { "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 0000000000..4974f90383 --- /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 0000000000..5a2c1479bb --- /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 0000000000..fc9b6a27aa --- /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 1dd7deaf8d..e0aa0b1768 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -33,7 +33,7 @@ using CallbackToken = scheduler::VSyncDispatch::CallbackToken; 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 2b69f13126..825f145ecd 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 @@ protected: 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 @@ SetFrameRateTest::SetFrameRateTest() { ::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::commitTransaction() { } } -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 364d8f1411..4708572dc4 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -110,7 +110,7 @@ public: 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 d83b9bbd40..fe0564e0d3 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -334,16 +334,14 @@ public: /* ------------------------------------------------------------------------ * 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 @@ public: }; 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 eefa11f1aa..ded75318ed 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -226,7 +226,7 @@ public: // 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 @@ TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) { 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 @@ TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) { 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 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) { 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 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemoveSignaledWithUnsignaledInt 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 ab893a3058..f5e3b77ca6 100644 --- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp @@ -81,7 +81,7 @@ TEST(TransactionProtoParserTest, parse) { 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 cae76849bf..ee7e92cbf6 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 @@ class ScreenCapture : public RefBase { 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 @@ public: 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 92250621ed..60c0cb5c3e 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -632,8 +632,10 @@ void CreateInfoWrapper::FilterExtension(const char* name) { 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 @@ void CreateInfoWrapper::FilterExtension(const char* name) { 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 @@ VkResult EnumerateInstanceExtensionProperties( 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 @@ VkResult EnumerateInstanceExtensionProperties( 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 5f37a50a03..b436db1de7 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -565,11 +565,13 @@ ProcHook::Extension GetProcHookExtension(const char* name) { 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 819f6b211c..079f9cca39 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -41,11 +41,13 @@ struct ProcHook { 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 4a6b4f12a1..0be4403731 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -619,42 +619,65 @@ VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/, 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 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, 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 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, 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 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, 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 @@ VkResult GetPhysicalDeviceSurfaceCapabilities2KHR( .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 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, int err; int query_value; - ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); + std::vector<VkPresentModeKHR> present_modes; + 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); + } 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_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); - 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; + 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); } - uint32_t max_buffer_count = static_cast<uint32_t>(query_value); - - std::vector<VkPresentModeKHR> present_modes; - 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 6a73023193..af56764a21 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -27,11 +27,13 @@ _INTERCEPTED_EXTENSIONS = [ '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', ] |