diff options
136 files changed, 2594 insertions, 1649 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 0e9ce897bc..24b201f8ab 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -2418,7 +2418,7 @@ void Dumpstate::DumpstateBoard(int out_fd) { // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we // will kill the HAL and grab whatever it dumped in time. - constexpr size_t timeout_sec = 30; + constexpr size_t timeout_sec = 45; if (dumpstate_hal_handle_aidl != nullptr) { DoDumpstateBoardAidl(dumpstate_hal_handle_aidl, dumpstate_fds, options_->bugreport_mode, diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 176c84d1e6..f32bb2403d 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; } @@ -723,35 +725,37 @@ binder::Status InstalldNativeService::createAppDataLocked( } if (flags & FLAG_STORAGE_SDK) { - auto status = createSdkSandboxDataDirectory(uuid, packageName, userId, appId, previousAppId, - seInfo, flags); - if (!status.isOk()) { - return status; + // Safe to ignore status since we can retry creating this by calling reconcileSdkData + auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, 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/misc_{ce|de}/user/0/sdksandbox/<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::createSdkSandboxDataDirectory( +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 appId, int32_t flags) { 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; } @@ -760,33 +764,16 @@ binder::Status InstalldNativeService::createSdkSandboxDataDirectory( // /data/misc_{ce,de}/<user-id>/sdksandbox directory gets created by vold // during user creation - // Prepare the app directory - auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, - packageName.c_str()); - if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) { - return error("Failed to prepare " + appPath); - } - - // Now prepare the shared directory which will be accessible by all codes - auto sharedPath = create_data_misc_sdk_sandbox_shared_path(uuid_, isCeData, userId, - packageName.c_str()); + // Prepare the package directory + 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 - 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 sdksandbox data")); + if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM)) { + return error("Failed to prepare " + packagePath); } - auto status = createAppDataDirs(sharedPath, sdkSandboxUid, &previousSdkSandboxUid, cacheGid, - seInfo, 0700); - if (!status.isOk()) { - return status; - } - - // TODO(b/211763739): We also need to handle art profile creations - - // TODO(b/211763739): And return the CE inode of the sdksandbox root directory and - // app directory under it so we can clear contents while CE storage is locked } return ok(); @@ -835,6 +822,107 @@ 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.subDirNames, 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>& subDirNames, + 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); + LOCK_PACKAGE_USER(); + +#if SDK_DEBUG + LOG(DEBUG) << "Creating per sdk data directory for: " << packageName; +#endif + + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + + // Prepare the sdk package directory in case it's missing + const auto status = + createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, 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); + + const auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, + packageName.c_str()); + + // Remove existing sub-directories not referred in subDirNames + const std::unordered_set<std::string> expectedSubDirNames(subDirNames.begin(), + subDirNames.end()); + const auto subDirHandler = [&packagePath, &expectedSubDirNames, + &res](const std::string& subDirName) { + // Remove the per-sdk directory if it is not referred in + // expectedSubDirNames + if (expectedSubDirNames.find(subDirName) == expectedSubDirNames.end()) { + auto path = packagePath + "/" + subDirName; + if (delete_dir_contents_and_dir(path) != 0) { + res = error("Failed to delete " + path); + return; + } + } + }; + const int ec = foreach_subdir(packagePath, subDirHandler); + if (ec != 0) { + res = error("Failed to process subdirs for " + packagePath); + continue; + } + + // Now create the subDirNames + for (const auto& subDirName : subDirNames) { + const std::string path = + create_data_misc_sdk_sandbox_sdk_path(uuid_, isCeData, userId, + packageName.c_str(), subDirName.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 | S_ISGID); + 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); @@ -980,6 +1068,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; } @@ -1076,6 +1205,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; } @@ -1561,6 +1716,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. @@ -1588,6 +1773,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; } @@ -1622,6 +1819,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) { @@ -1638,6 +1840,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); @@ -1890,6 +2097,13 @@ static void collectQuotaStats(const std::string& uuid, int32_t userId, stats->dataSize += space; } + int sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); + if (sdkSandboxUid != -1) { + if ((space = GetOccupiedSpaceForUid(uuid, sdkSandboxUid)) != -1) { + stats->dataSize += space; + } + } + int cacheGid = multiuser_get_cache_gid(userId, appId); if (cacheGid != -1) { if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { @@ -1997,8 +2211,17 @@ static void collectManualStats(const std::string& path, struct stats* stats) { closedir(d); } +void collectManualStatsForSubDirectories(const std::string& path, struct stats* stats) { + const auto subDirHandler = [&path, &stats](const std::string& subDir) { + auto fullpath = path + "/" + subDir; + collectManualStats(fullpath, stats); + }; + foreach_subdir(path, subDirHandler); +} + static void collectManualStatsForUser(const std::string& path, struct stats* stats, - bool exclude_apps = false) { + bool exclude_apps = false, + bool is_sdk_sandbox_storage = false) { DIR *d; int dfd; struct dirent *de; @@ -2023,6 +2246,11 @@ static void collectManualStatsForUser(const std::string& path, struct stats* sta continue; } else if (exclude_apps && (user_uid >= AID_APP_START && user_uid <= AID_APP_END)) { continue; + } else if (is_sdk_sandbox_storage) { + // In case of sdk sandbox storage (e.g. /data/misc_ce/0/sdksandbox/<package-name>), + // collect individual stats of each subdirectory (shared, storage of each sdk etc.) + collectManualStatsForSubDirectories(StringPrintf("%s/%s", path.c_str(), name), + stats); } else { collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats); } @@ -2165,6 +2393,19 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string collectManualStats(dePath, &stats); ATRACE_END(); + // In case of sdk sandbox storage (e.g. /data/misc_ce/0/sdksandbox/<package-name>), + // collect individual stats of each subdirectory (shared, storage of each sdk etc.) + if (appId >= AID_APP_START && appId <= AID_APP_END) { + ATRACE_BEGIN("sdksandbox"); + auto sdkSandboxCePath = + create_data_misc_sdk_sandbox_package_path(uuid_, true, userId, pkgname); + collectManualStatsForSubDirectories(sdkSandboxCePath, &stats); + auto sdkSandboxDePath = + create_data_misc_sdk_sandbox_package_path(uuid_, false, userId, pkgname); + collectManualStatsForSubDirectories(sdkSandboxDePath, &stats); + ATRACE_END(); + } + if (!uuid) { ATRACE_BEGIN("profiles"); calculate_tree_size( @@ -2401,6 +2642,13 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin collectManualStatsForUser(dePath, &stats); ATRACE_END(); + ATRACE_BEGIN("sdksandbox"); + auto sdkSandboxCePath = create_data_misc_sdk_sandbox_path(uuid_, true, userId); + collectManualStatsForUser(sdkSandboxCePath, &stats, false, true); + auto sdkSandboxDePath = create_data_misc_sdk_sandbox_path(uuid_, false, userId); + collectManualStatsForUser(sdkSandboxDePath, &stats, false, true); + ATRACE_END(); + if (!uuid) { ATRACE_BEGIN("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); @@ -2920,6 +3168,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) { @@ -3269,11 +3560,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 c9a4d50179..5c2184252c 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,11 +204,28 @@ 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 createSdkSandboxDataDirectory(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, - int32_t appId, int32_t previousAppId, - const std::string& seInfo, int32_t flags); + binder::Status createSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid, + const std::string& packageName, + int32_t userId, int32_t appId, + 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 flags); + binder::Status reconcileSdkData(const std::optional<std::string>& uuid, + const std::string& packageName, + const std::vector<std::string>& subDirNames, 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/TEST_MAPPING b/cmds/installd/TEST_MAPPING index 3f0fb6d2ba..8ccab4cc2d 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -30,6 +30,9 @@ }, { "name": "CtsCompilationTestCases" + }, + { + "name": "SdkSandboxStorageHostTest" } ] } diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 428e2b3908..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, diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl new file mode 100644 index 0000000000..583a36d580 --- /dev/null +++ b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl @@ -0,0 +1,29 @@ +/* + * 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> subDirNames; + int userId; + int appId; + int previousAppId; + @utf8InCpp String seInfo; + int flags; +} 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_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 31f5dcef4a..38cb37046d 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; +using std::filesystem::is_empty; namespace android { std::string get_package_name(uid_t uid) { @@ -75,13 +79,18 @@ 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 const std::string 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; +static constexpr const int FLAG_CLEAR_CACHE_ONLY = InstalldNativeService::FLAG_CLEAR_CACHE_ONLY; +static constexpr const int FLAG_CLEAR_CODE_CACHE_ONLY = + InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY; const gid_t kTestAppUid = multiuser_get_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 @@ -104,18 +113,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.c_str(), 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); @@ -123,8 +132,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) { @@ -139,7 +148,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; } @@ -162,8 +171,8 @@ 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) { +static bool exists_renamed_deleted_dir(const std::string& rootDirectory) { + return find_file((kTestPath + rootDirectory).c_str(), [](const std::string& name, bool is_dir) { return is_dir && is_renamed_deleted_dir(name); }); } @@ -180,197 +189,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)); @@ -390,7 +407,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)); @@ -400,12 +417,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)); @@ -415,12 +432,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)); } @@ -952,23 +969,23 @@ TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { 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"; @@ -976,6 +993,22 @@ public: return args; } + android::os::ReconcileSdkDataArgs reconcileSdkDataArgs( + const std::string& packageName, const std::vector<std::string>& subDirNames) { + android::os::ReconcileSdkDataArgs args; + args.uuid = kTestUuid; + args.packageName = packageName; + for (const auto& subDirName : subDirNames) { + args.subDirNames.push_back(subDirName); + } + args.userId = kTestUserId; + args.appId = kTestAppId; + args.previousAppId = -1; + args.seInfo = "default"; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + return args; + } + protected: InstalldNativeService* service; @@ -1000,41 +1033,30 @@ 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(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData) { +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 | FLAG_STORAGE_SDK; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); // Create the app user data. ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); + const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo"; + CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751); - CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751); - CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700); - CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); - CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); + const std::string fooDePath = "misc_de/0/sdksandbox/com.foo"; + CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751); } -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutSdkFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlag) { android::os::CreateAppDataResult result; - android::os::CreateAppDataArgs args = createAppDataArgs(); - args.packageName = "com.foo"; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; // Create the app user data. @@ -1044,26 +1066,38 @@ TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutSdkFlag) ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); } -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutDeFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlagDeletesExisting) { android::os::CreateAppDataResult result; - android::os::CreateAppDataArgs args = createAppDataArgs(); - args.packageName = "com.foo"; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + 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_CreatesSdkPackageData_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/sdksandbox/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/sdksandbox/com.foo")); } -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutCeFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutCeFlag) { android::os::CreateAppDataResult result; - android::os::CreateAppDataArgs args = createAppDataArgs(); - args.packageName = "com.foo"; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK; // Create the app user data. @@ -1073,7 +1107,223 @@ TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutCeFlag) ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); // Only DE paths should exist - CheckFileAccess("misc_de/0/sdksandbox/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@random1", "baz@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 | S_ISGID | 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 | S_ISGID | 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 | S_ISGID | 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 | S_ISGID | 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_ExtraCodeDirectoriesAreDeleted) { + android::os::ReconcileSdkDataArgs args = + reconcileSdkDataArgs("com.foo", {"bar@random1", "baz@random2"}); + + // Create the sdksandbox data. + ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args)); + + // Retry with different package name + args.subDirNames[0] = "bar.diff@random1"; + + // 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 | S_ISGID | 0700); + CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid, + S_IFDIR | S_ISGID | 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, 0700)); + ASSERT_TRUE(mkdirs(dePackagePath, 0700)); + const std::vector<std::string> packagePaths = {cePackagePath, dePackagePath}; + for (const auto& packagePath : packagePaths) { + for (auto sdkName : sdkNames) { + ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/cache", 0700)); + ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/code_cache", 0700)); + std::ofstream{packagePath + "/" + sdkName + "/cache/cachedTestData.txt"}; + std::ofstream{packagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"}; + } + } + } +}; + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_CE | FLAG_CLEAR_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCodeCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_CE | FLAG_CLEAR_CODE_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data + ASSERT_BINDER_SUCCESS( + service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_DE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY), + -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCodeCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_DE | FLAG_CLEAR_CODE_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndWithoutAnyCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_CE, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndWithoutAnyCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_DE, -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1")); + ASSERT_TRUE(is_empty(packagePath + "/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 17802a30e3..910cd630f3 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -21,6 +21,7 @@ #include <android-base/logging.h> #include <android-base/scopeguard.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include "InstalldNativeService.h" @@ -47,6 +48,8 @@ namespace android { namespace installd { +using ::testing::UnorderedElementsAre; + class UtilsTest : public testing::Test { protected: virtual void SetUp() { @@ -658,6 +661,23 @@ TEST_F(UtilsTest, TestCreateDirIfNeeded) { ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700)); } +TEST_F(UtilsTest, TestForEachSubdir) { + auto deleter = [&]() { + delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + system("mkdir -p /data/local/tmp/user/0/com.foo"); + system("mkdir -p /data/local/tmp/user/0/com.bar"); + system("touch /data/local/tmp/user/0/some-file"); + + std::vector<std::string> result; + foreach_subdir("/data/local/tmp/user/0", + [&](const std::string &filename) { result.push_back(filename); }); + + EXPECT_THAT(result, UnorderedElementsAre("com.foo", "com.bar")); +} + TEST_F(UtilsTest, TestSdkSandboxDataPaths) { // Ce data paths EXPECT_EQ("/data/misc_ce/0/sdksandbox", @@ -670,9 +690,11 @@ TEST_F(UtilsTest, TestSdkSandboxDataPaths) { 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")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 0, "com.foo", "shared")); EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared", - create_data_misc_sdk_sandbox_shared_path(nullptr, true, 10, "com.foo")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "shared")); + 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/sdksandbox", @@ -685,9 +707,11 @@ TEST_F(UtilsTest, TestSdkSandboxDataPaths) { 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")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 0, "com.foo", "shared")); EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared", - create_data_misc_sdk_sandbox_shared_path(nullptr, false, 10, "com.foo")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "shared")); + 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) { diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 992425d56e..123e3d4181 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -212,7 +212,7 @@ std::string create_data_misc_sdk_sandbox_path(const char* uuid, bool isCeData, u /** * Create the path name where code data for all codes in a particular app will be stored. - * E.g. /data/misc_ce/0/sdksandbox/<app-name> + * E.g. /data/misc_ce/0/sdksandbox/<package-name> */ std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData, userid_t user, const char* package_name) { @@ -223,15 +223,17 @@ std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, b } /** - * Create the path name where shared code data for a particular app will be stored. - * E.g. /data/misc_ce/0/sdksandbox/<app-name>/shared + * Create the path name where sdk data for a particular sdk will be stored. + * E.g. /data/misc_ce/0/sdksandbox/<package-name>/com.foo@randomstrings */ -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", +std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData, + userid_t user, const char* package_name, + const char* sub_dir_name) { + return StringPrintf("%s/%s", create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user, package_name) - .c_str()); + .c_str(), + sub_dir_name); } std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) { @@ -696,6 +698,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) { diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 4b56f99de2..cb3099337c 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 @@ -64,8 +65,9 @@ std::string create_data_misc_sdk_sandbox_path(const char* volume_uuid, bool isCe 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* sub_dir_name); std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user); std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user); @@ -130,6 +132,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, diff --git a/include/android/storage_manager.h b/include/android/storage_manager.h index 7f2ee08d62..270570e0df 100644 --- a/include/android/storage_manager.h +++ b/include/android/storage_manager.h @@ -124,6 +124,12 @@ typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const int3 /** * Attempts to mount an OBB file. This is an asynchronous operation. + * + * Since API level 33, this function can only be used to mount unencrypted OBBs, + * i.e. the {@code key} parameter must be {@code null} or an empty string. Note + * that even before API level 33, mounting encrypted OBBs didn't work on many + * Android device implementations. Applications should not assume any particular + * behavior when {@code key} is nonempty. */ void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key, AStorageManager_obbCallbackFunc cb, void* data); 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/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index e1cbc1996d..8132d46940 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -18,10 +18,13 @@ #include <errno.h> #include <fcntl.h> +#include <linux/memfd.h> #include <stdint.h> #include <stdlib.h> #include <sys/ioctl.h> +#include <sys/mman.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> @@ -34,6 +37,24 @@ namespace android { // --------------------------------------------------------------------------- +#ifdef __BIONIC__ +static int memfd_create_region(const char* name, size_t size) { + int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd == -1) { + ALOGE("%s: memfd_create(%s, %zd) failed: %s\n", __func__, name, size, strerror(errno)); + return -1; + } + + if (ftruncate(fd, size) == -1) { + ALOGE("%s, ftruncate(%s, %zd) failed for memfd creation: %s\n", __func__, name, size, + strerror(errno)); + close(fd); + return -1; + } + return fd; +} +#endif + MemoryHeapBase::MemoryHeapBase() : mFD(-1), mSize(0), mBase(MAP_FAILED), mDevice(nullptr), mNeedUnmap(false), mOffset(0) @@ -45,15 +66,36 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) mDevice(nullptr), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size); - ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); - if (fd >= 0) { - if (mapfd(fd, true, size) == NO_ERROR) { - if (flags & READ_ONLY) { - ashmem_set_prot_region(fd, PROT_READ); - } + size = ((size + pagesize - 1) & ~(pagesize - 1)); + int fd = -1; + if (mFlags & FORCE_MEMFD) { +#ifdef __BIONIC__ + ALOGV("MemoryHeapBase: Attempting to force MemFD"); + fd = memfd_create_region(name ? name : "MemoryHeapBase", size); + if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return; + const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | + ((mFlags & MEMFD_ALLOW_SEALING) ? 0 : F_SEAL_SEAL); + if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) { + ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name, + SEAL_FLAGS, strerror(errno)); + munmap(mBase, mSize); + mBase = nullptr; + mSize = 0; + close(fd); } + return; +#else + mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING); +#endif + } + if (mFlags & MEMFD_ALLOW_SEALING) { + LOG_ALWAYS_FATAL("Invalid Flags. MEMFD_ALLOW_SEALING only valid with FORCE_MEMFD."); + } + fd = ashmem_create_region(name ? name : "MemoryHeapBase", size); + ALOGE_IF(fd < 0, "MemoryHeapBase: error creating ashmem region: %s", strerror(errno)); + if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return; + if (mFlags & READ_ONLY) { + ashmem_set_prot_region(fd, PROT_READ); } } @@ -61,6 +103,9 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { + if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) { + LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); + } int open_flags = O_RDWR; if (flags & NO_CACHING) open_flags |= O_SYNC; @@ -80,6 +125,9 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { + if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) { + LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); + } const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), false, size, offset); diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 6a138e340f..a217a157c9 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)) { @@ -1862,6 +1862,7 @@ status_t Parcel::readStrongBinder(sp<IBinder>* val) const { status_t status = readNullableStrongBinder(val); if (status == OK && !val->get()) { + ALOGW("Expecting binder but got null!"); status = UNEXPECTED_NULL; } return status; diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp index 2e86b7415a..3cf94e3047 100644 --- a/libs/binder/ParcelableHolder.cpp +++ b/libs/binder/ParcelableHolder.cpp @@ -52,7 +52,10 @@ status_t ParcelableHolder::writeToParcel(Parcel* p) const { } status_t ParcelableHolder::readFromParcel(const Parcel* p) { - this->mStability = static_cast<Stability>(p->readInt32()); + int32_t wireStability; + if (status_t status = p->readInt32(&wireStability); status != OK) return status; + if (static_cast<int32_t>(this->mStability) != wireStability) return BAD_VALUE; + this->mParcelable = nullptr; this->mParcelableName = std::nullopt; int32_t rawDataSize; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index baa817c6b9..b14a838447 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -409,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) { @@ -452,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..d40778a3d8 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; @@ -848,10 +853,16 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co } if (session->mConnections.mOutgoing.size() == 0) { - ALOGE("Session has no client connections. This is required for an RPC server to make " - "any non-nested (e.g. oneway or on another thread) calls. Use: %d. Server " - "connections: %zu", - static_cast<int>(use), session->mConnections.mIncoming.size()); + ALOGE("Session has no outgoing connections. This is required for an RPC server to make " + "any non-nested (e.g. oneway or on another thread) calls. Use code request " + "reason: %d. Incoming connections: %zu. %s.", + static_cast<int>(use), session->mConnections.mIncoming.size(), + (session->server() + ? "This is a server session, so see RpcSession::setMaxIncomingThreads " + "for the corresponding client" + : "This is a client session, so see RpcSession::setMaxOutgoingThreads " + "for this client or RpcServer::setMaxThreads for the corresponding " + "server")); return WOULD_BLOCK; } 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/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index dd76943ac7..15dd28f08e 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -34,7 +34,21 @@ public: // memory won't be mapped locally, but will be mapped in the remote // process. DONT_MAP_LOCALLY = 0x00000100, - NO_CACHING = 0x00000200 + NO_CACHING = 0x00000200, + // Bypass ashmem-libcutils to create a memfd shared region. + // Ashmem-libcutils will eventually migrate to memfd. + // Memfd has security benefits and supports file sealing. + // Calling process will need to modify selinux permissions to + // open access to tmpfs files. See audioserver for examples. + // This is only valid for size constructor. + // For host compilation targets, memfd is stubbed in favor of /tmp + // files so sealing is not enforced. + FORCE_MEMFD = 0x00000400, + // Default opt-out of sealing behavior in memfd to avoid potential DOS. + // Clients of shared files can seal at anytime via syscall, leading to + // TOC/TOU issues if additional seals prevent access from the creating + // process. Alternatively, seccomp fcntl(). + MEMFD_ALLOW_SEALING = 0x00000800 }; /* 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..f45aa7631b 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,13 +91,22 @@ 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; } - RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability)); + parcelable_stability_t wireStability; + RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability)); + if (this->mStability != wireStability) { + return STATUS_BAD_VALUE; + } + int32_t dataSize; binder_status_t status = AParcel_readInt32(parcel, &dataSize); @@ -99,7 +120,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 +140,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 +158,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 +186,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/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs index d58e839ad4..432da5dfd7 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -233,7 +233,9 @@ impl Parcelable for ParcelableHolder { } fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> { - self.stability = parcel.read()?; + if self.stability != parcel.read()? { + return Err(StatusCode::BAD_VALUE); + } let data_size: i32 = parcel.read()?; if data_size < 0 { diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index ff55d6ebdc..a3533d831b 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -99,6 +99,7 @@ cc_test { "binderParcelUnitTest.cpp", "binderBinderUnitTest.cpp", "binderStatusUnitTest.cpp", + "binderMemoryHeapBaseUnitTest.cpp", ], shared_libs: [ "libbinder", diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp new file mode 100644 index 0000000000..21cb70be17 --- /dev/null +++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/MemoryHeapBase.h> +#include <cutils/ashmem.h> +#include <fcntl.h> + +#include <gtest/gtest.h> +using namespace android; +#ifdef __BIONIC__ +TEST(MemoryHeapBase, ForceMemfdRespected) { + auto mHeap = sp<MemoryHeapBase>::make(10, MemoryHeapBase::FORCE_MEMFD, "Test mapping"); + int fd = mHeap->getHeapID(); + EXPECT_NE(fd, -1); + EXPECT_FALSE(ashmem_valid(fd)); + EXPECT_NE(fcntl(fd, F_GET_SEALS), -1); +} + +TEST(MemoryHeapBase, MemfdSealed) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::FORCE_MEMFD, + "Test mapping"); + int fd = mHeap->getHeapID(); + EXPECT_NE(fd, -1); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL); +} + +TEST(MemoryHeapBase, MemfdUnsealed) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::FORCE_MEMFD | + MemoryHeapBase::MEMFD_ALLOW_SEALING, + "Test mapping"); + int fd = mHeap->getHeapID(); + EXPECT_NE(fd, -1); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0); +} + +TEST(MemoryHeapBase, MemfdSealedProtected) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::FORCE_MEMFD | + MemoryHeapBase::READ_ONLY, + "Test mapping"); + int fd = mHeap->getHeapID(); + EXPECT_NE(fd, -1); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE); +} + +TEST(MemoryHeapBase, MemfdUnsealedProtected) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::FORCE_MEMFD | + MemoryHeapBase::READ_ONLY | + MemoryHeapBase::MEMFD_ALLOW_SEALING, + "Test mapping"); + int fd = mHeap->getHeapID(); + EXPECT_NE(fd, -1); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE); +} + +#else +TEST(MemoryHeapBase, HostMemfdExpected) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::READ_ONLY, + "Test mapping"); + int fd = mHeap->getHeapID(); + void* ptr = mHeap->getBase(); + EXPECT_NE(ptr, MAP_FAILED); + EXPECT_TRUE(ashmem_valid(fd)); + EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY); +} + +TEST(MemoryHeapBase,HostMemfdException) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::FORCE_MEMFD | + MemoryHeapBase::READ_ONLY | + MemoryHeapBase::MEMFD_ALLOW_SEALING, + "Test mapping"); + int fd = mHeap->getHeapID(); + void* ptr = mHeap->getBase(); + EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY); + EXPECT_TRUE(ashmem_valid(fd)); + EXPECT_NE(ptr, MAP_FAILED); +} + +#endif diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 38bde3a011..57d496d2fb 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -66,10 +66,13 @@ cc_library_static { srcs: [ "random_fd.cpp", "random_parcel.cpp", + "libbinder_driver.cpp", + "libbinder_ndk_driver.cpp", ], shared_libs: [ "libbase", "libbinder", + "libbinder_ndk", "libcutils", "libutils", ], diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h new file mode 100644 index 0000000000..a9a6197439 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.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 <binder/IBinder.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { +/** + * Based on the random data in provider, construct an arbitrary number of + * Parcel objects and send them to the service in serial. + * + * Usage: + * + * extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + * FuzzedDataProvider provider = FuzzedDataProvider(data, size); + * // can use provider here to create a service with different options + * sp<IFoo> myService = sp<IFoo>::make(...); + * fuzzService(myService, std::move(provider)); + * } + */ +void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider); +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h new file mode 100644 index 0000000000..f2b782337c --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.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 <android/binder_parcel.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { +/** + * Based on the random data in provider, construct an arbitrary number of + * Parcel objects and send them to the service in serial. + * + * Usage: + * + * extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + * FuzzedDataProvider provider = FuzzedDataProvider(data, size); + * // can use provider here to create a service with different options + * std::shared_ptr<IFoo> myService = ndk::SharedRefBase<IFoo>::make(...); + * fuzzService(myService->asBinder().get(), std::move(provider)); + * } + */ +void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider); +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp new file mode 100644 index 0000000000..e849c9bbce --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -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. + */ +#include <fuzzbinder/libbinder_driver.h> + +#include <fuzzbinder/random_parcel.h> + +namespace android { + +void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { + while (provider.remaining_bytes() > 0) { + uint32_t code = provider.ConsumeIntegral<uint32_t>(); + uint32_t flags = provider.ConsumeIntegral<uint32_t>(); + Parcel data; + + std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>( + provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); + fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size())); + + Parcel reply; + (void)binder->transact(code, data, &reply, flags); + } +} + +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp new file mode 100644 index 0000000000..462ef9a5e9 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp @@ -0,0 +1,31 @@ +/* + * 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 <fuzzbinder/libbinder_ndk_driver.h> + +#include <fuzzbinder/libbinder_driver.h> +#include <fuzzbinder/random_parcel.h> + +// libbinder_ndk doesn't export this header which breaks down its API for NDK +// and APEX users, but we need access to it to fuzz. +#include "../../ndk/ibinder_internal.h" + +namespace android { + +void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) { + fuzzService(binder->getBinder(), std::move(provider)); +} + +} // namespace android 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 8e23eb8766..ec4c7c10a4 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -182,7 +182,8 @@ 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, @@ -230,7 +231,8 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, } } 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); } } @@ -551,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 @@ -857,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, @@ -1050,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 5ab0abc561..5532c6e747 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -111,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 { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 6944d38dfe..338ff1114f 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -796,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; } @@ -832,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 26ccda580a..27856cef13 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -930,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; } @@ -984,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 @@ -1399,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, diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 265ae24255..65fc04df1a 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -254,6 +254,21 @@ private: // 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/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 4dfc383b57..0a3cc19a13 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -113,6 +113,7 @@ public: // android.permission.ACCESS_SURFACE_FLINGER eEarlyWakeupStart = 0x08, eEarlyWakeupEnd = 0x10, + eOneWay = 0x20 }; enum VsyncSource { diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 885b4ae55c..0f37dab53c 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -89,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 diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 6c79b5bbbc..c8ac1662ad 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -461,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); @@ -521,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/input/Android.bp b/libs/input/Android.bp index 606fe2a59d..18fb7c1bfb 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -112,7 +112,7 @@ cc_library { "frameworks/native/libs/arect/include", ], }, - linux_glibc: { + host_linux: { srcs: [ "InputTransport.cpp", "android/os/IInputConstants.aidl", diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index bfdd22c3df..06ad6a8f61 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -160,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 @@ -364,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) { @@ -568,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), @@ -586,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); @@ -943,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) diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index d3e171a2d8..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); /** diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 470d2f686d..9633932e75 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -65,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; @@ -489,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()); @@ -4451,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) { 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/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp index 1100cad361..6e5e14de28 100644 --- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp +++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp @@ -66,13 +66,13 @@ static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower: sp<IPower> hal = waitForVintfService<IPower>(); if (hal == nullptr) { - ALOGI("Power HAL not available, skipping test..."); + ALOGV("Power HAL not available, skipping test..."); return; } binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...); if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) { - ALOGI("Power HAL does not support this operation, skipping test..."); + ALOGV("Power HAL does not support this operation, skipping test..."); return; } @@ -93,7 +93,7 @@ static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::* sp<IPower> pwHal = waitForVintfService<IPower>(); if (pwHal == nullptr) { - ALOGI("Power HAL not available, skipping test..."); + ALOGV("Power HAL not available, skipping test..."); return; } @@ -105,13 +105,13 @@ static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::* auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal); if (hal == nullptr) { - ALOGI("Power HAL doesn't support session, skipping test..."); + ALOGV("Power HAL doesn't support session, skipping test..."); return; } binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...); if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) { - ALOGI("Power HAL does not support this operation, skipping test..."); + ALOGV("Power HAL does not support this operation, skipping test..."); return; } @@ -159,13 +159,13 @@ static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) sp<IPower> hal = waitForVintfService<IPower>(); if (hal == nullptr) { - ALOGI("Power HAL not available, skipping test..."); + ALOGV("Power HAL not available, skipping test..."); return; } binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession); if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) { - ALOGI("Power HAL does not support this operation, skipping test..."); + ALOGV("Power HAL does not support this operation, skipping test..."); return; } diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp index 97e026bd25..167f3a64af 100644 --- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp +++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp @@ -51,7 +51,7 @@ static void runBenchmark(benchmark::State& state, microseconds delay, Return<R> sp<I> hal = I::getService(); if (hal == nullptr) { - ALOGI("Power HAL HIDL not available, skipping test..."); + ALOGV("Power HAL HIDL not available, skipping test..."); return; } diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 18a6baeffe..0b23a5a5bb 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; } @@ -361,6 +362,8 @@ void BufferLayer::onPostComposition(const DisplayDevice* display, // composition. if (!mBufferInfo.mFrameLatencyNeeded) return; + mAlreadyDisplayedThisCompose = false; + // Update mFrameEventHistory. { Mutex::Autolock lock(mFrameEventHistoryMutex); diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index e6a76e8ea9..bcae8d9564 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -75,17 +75,15 @@ BufferStateLayer::~BufferStateLayer() { // ----------------------------------------------------------------------- void BufferStateLayer::onLayerDisplayed( std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) { - // If a layer has been displayed again we may need to clear - // the mLastClientComposition fence that we use for early release in setBuffer - // (as we now have a new fence which won't pass through the client composition path in some cases - // e.g. screenshot). We expect one call to onLayerDisplayed after receiving the GL comp fence - // from a single composition cycle, and want to clear on the second call - // (which we track with mLastClientCompositionDisplayed) - if (mLastClientCompositionDisplayed) { + // If we are displayed on multiple displays in a single composition cycle then we would + // need to do careful tracking to enable the use of the mLastClientCompositionFence. + // For example we can only use it if all the displays are client comp, and we need + // to merge all the client comp fences. We could do this, but for now we just + // disable the optimization when a layer is composed on multiple displays. + if (mAlreadyDisplayedThisCompose) { mLastClientCompositionFence = nullptr; - mLastClientCompositionDisplayed = false; - } else if (mLastClientCompositionFence) { - mLastClientCompositionDisplayed = true; + } else { + mAlreadyDisplayedThisCompose = true; } // The previous release fence notifies the client that SurfaceFlinger is done with the previous 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 9302b7bc37..aefc014062 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -41,10 +41,6 @@ cc_defaults { "libtonemap", "libtrace_proto", "libaidlcommonsupport", - "libprocessgroup", - "libcgrouprc", - "libjsoncpp", - "libcgrouprc_format", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", @@ -72,7 +68,6 @@ 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/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h index ca86f4c604..c553fce85d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h @@ -72,9 +72,6 @@ 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 15551029b3..d8644a428d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -35,7 +35,6 @@ #include <utils/Vector.h> #include <ui/DisplayIdentification.h> -#include "DisplayHardware/HWComposer.h" namespace android { @@ -55,7 +54,6 @@ struct LayerFECompositionState; namespace impl { struct OutputCompositionState; -struct GpuCompositionResult; } // namespace impl /** @@ -264,9 +262,6 @@ 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; @@ -283,22 +278,13 @@ 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&, GpuCompositionResult&&) = 0; + virtual void finishFrame(const CompositionRefreshArgs&) = 0; virtual std::optional<base::unique_fd> composeSurfaces( - const Region&, const compositionengine::CompositionRefreshArgs&, - std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0; + const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0; virtual void postFramebuffer() = 0; virtual void renderCachedSets(const CompositionRefreshArgs&) = 0; - virtual std::optional<android::HWComposer::DeviceRequestedChanges> - chooseCompositionStrategy() = 0; - virtual void applyCompositionStrategy( - const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0; + virtual void chooseCompositionStrategy() = 0; virtual bool getSkipColorTransform() const = 0; virtual FrameFences presentAndGetFrameFences() = 0; virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( @@ -309,7 +295,6 @@ 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 9ee779cca1..daee83bd2c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h @@ -100,9 +100,6 @@ 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 3b8b06fc2f..58d2530877 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -22,7 +22,6 @@ #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> @@ -52,14 +51,11 @@ public: void setReleasedLayers(const CompositionRefreshArgs&) override; void setColorTransform(const CompositionRefreshArgs&) override; void setColorProfile(const ColorProfile&) override; - - using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges; - std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override; - void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override; + void chooseCompositionStrategy() override; bool getSkipColorTransform() const override; compositionengine::Output::FrameFences presentAndGetFrameFences() override; void setExpensiveRenderingExpected(bool) override; - void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override; + void finishFrame(const CompositionRefreshArgs&) override; // compositionengine::Display overrides DisplayId getId() const override; @@ -77,6 +73,7 @@ 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&); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h deleted file mode 100644 index 2b1f50ff57..0000000000 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 { - // 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 deleted file mode 100644 index 11c0054089..0000000000 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 0be5d018f6..a7a8e97be5 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -17,13 +17,9 @@ #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> @@ -96,38 +92,25 @@ 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&, GpuCompositionResult&&) override; - std::optional<base::unique_fd> composeSurfaces(const Region&, - const compositionengine::CompositionRefreshArgs&, - std::shared_ptr<renderengine::ExternalTexture>, - base::unique_fd&) override; + void finishFrame(const CompositionRefreshArgs&) override; + std::optional<base::unique_fd> composeSurfaces( + const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) 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; - using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges; - std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override; - void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{}; + void chooseCompositionStrategy() override; bool getSkipColorTransform() const override; compositionengine::Output::FrameFences presentAndGetFrameFences() override; std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( @@ -148,7 +131,6 @@ 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; @@ -162,7 +144,6 @@ 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 ade9b25b63..66dd825e5b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -37,8 +37,6 @@ #include <ui/Region.h> #include <ui/Transform.h> -#include "DisplayHardware/HWComposer.h" - namespace android { namespace compositionengine::impl { @@ -116,8 +114,6 @@ 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; @@ -141,18 +137,6 @@ struct OutputCompositionState { // This is slightly distinct from nits, in that nits cannot be passed to hw composer. std::optional<float> displayBrightness = std::nullopt; - enum class CompositionStrategyPredictionState : uint32_t { - // Composition strategy prediction did not run for this frame. - DISABLED = 0, - // Composition strategy predicted successfully for this frame. - SUCCESS = 1, - // Composition strategy prediction failed for this frame. - FAIL = 2, - }; - - CompositionStrategyPredictionState strategyPrediction = - CompositionStrategyPredictionState::DISABLED; - // Debugging void dump(std::string& result) const; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h index e4cb113645..a8a538003e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h @@ -63,7 +63,6 @@ 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 72e6f3bdbb..d90cc909ba 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -41,7 +41,6 @@ public: 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 27303a82be..b68b95d07b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -22,7 +22,6 @@ #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> @@ -100,22 +99,16 @@ public: MOCK_METHOD0(beginFrame, void()); MOCK_METHOD0(prepareFrame, 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_METHOD0(chooseCompositionStrategy, void()); MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&)); - MOCK_METHOD2(finishFrame, - void(const compositionengine::CompositionRefreshArgs&, GpuCompositionResult&&)); + MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&)); - MOCK_METHOD4(composeSurfaces, + MOCK_METHOD2(composeSurfaces, std::optional<base::unique_fd>( const Region&, - const compositionengine::CompositionRefreshArgs& refreshArgs, - std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&)); + const compositionengine::CompositionRefreshArgs& refreshArgs)); MOCK_CONST_METHOD0(getSkipColorTransform, bool()); MOCK_METHOD0(postFramebuffer, void()); @@ -128,8 +121,6 @@ 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 e12aebb50c..fe858c2817 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h @@ -45,7 +45,6 @@ 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 09648c356d..6a75283f7b 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -221,12 +221,12 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& setReleasedLayers(std::move(releasedLayers)); } -std::optional<android::HWComposer::DeviceRequestedChanges> Display::chooseCompositionStrategy() { +void Display::chooseCompositionStrategy() { ATRACE_CALL(); ALOGV(__FUNCTION__); if (mIsDisconnected) { - return {}; + return; } // Default to the base settings -- client composition only. @@ -235,7 +235,7 @@ std::optional<android::HWComposer::DeviceRequestedChanges> Display::chooseCompos // 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,13 +260,8 @@ std::optional<android::HWComposer::DeviceRequestedChanges> Display::chooseCompos 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); @@ -292,6 +287,12 @@ 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(), @@ -389,8 +390,7 @@ void Display::setExpensiveRenderingExpected(bool enabled) { } } -void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs, - GpuCompositionResult&& result) { +void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) { // 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, std::move(result)); + impl::Output::finishFrame(refreshArgs); } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp index 28900af162..db6d4f2fed 100644 --- a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp @@ -20,8 +20,4 @@ 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 deleted file mode 100644 index 497424a327..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 e4bd325f99..aef55d49fe 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -22,7 +22,6 @@ #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> @@ -58,8 +57,7 @@ namespace android::compositionengine { Output::~Output() = default; namespace impl { -using CompositionStrategyPredictionState = - OutputCompositionState::CompositionStrategyPredictionState; + namespace { template <typename T> @@ -436,17 +434,9 @@ void Output::present(const compositionengine::CompositionRefreshArgs& refreshArg writeCompositionState(refreshArgs); setColorTransform(refreshArgs); beginFrame(); - - GpuCompositionResult result; - const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs); - if (predictCompositionStrategy) { - result = prepareFrameAsync(refreshArgs); - } else { - prepareFrame(); - } - + prepareFrame(); devOptRepaintFlash(refreshArgs); - finishFrame(refreshArgs, std::move(result)); + finishFrame(refreshArgs); postFramebuffer(); renderCachedSets(refreshArgs); } @@ -961,64 +951,19 @@ void Output::prepareFrame() { ATRACE_CALL(); ALOGV(__FUNCTION__); - auto& outputState = editState(); + const auto& outputState = getState(); if (!outputState.isEnabled) { return; } - auto changes = chooseCompositionStrategy(); - outputState.strategyPrediction = CompositionStrategyPredictionState::DISABLED; - outputState.previousDeviceRequestedChanges = changes; - if (changes) { - applyCompositionStrategy(changes); - } - finishPrepareFrame(); -} + chooseCompositionStrategy(); -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); - } + if (mPlanner) { + mPlanner->reportFinalPlan(getOutputLayersOrderedByZ()); } - auto changes = hwcResult.valid() ? hwcResult.get() : std::nullopt; - const bool predictionSucceeded = dequeueSucceeded && changes == previousChanges; - state.strategyPrediction = predictionSucceeded ? CompositionStrategyPredictionState::SUCCESS - : CompositionStrategyPredictionState::FAIL; - 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; + mRenderSurface->prepareFrame(outputState.usesClientComposition, + outputState.usesDeviceComposition); } void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) { @@ -1028,11 +973,7 @@ void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& if (getState().isEnabled) { if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) { - base::unique_fd bufferFence; - std::shared_ptr<renderengine::ExternalTexture> buffer; - updateProtectedContentState(); - dequeueRenderBuffer(&bufferFence, &buffer); - static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs, buffer, bufferFence)); + static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs)); mRenderSurface->queueBuffer(base::unique_fd()); } } @@ -1044,33 +985,17 @@ void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& prepareFrame(); } -void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) { +void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) { ATRACE_CALL(); ALOGV(__FUNCTION__); - const auto& outputState = getState(); - if (!outputState.isEnabled) { + + if (!getState().isEnabled) { return; } - std::optional<base::unique_fd> optReadyFence; - std::shared_ptr<renderengine::ExternalTexture> buffer; - base::unique_fd bufferFence; - if (outputState.strategyPrediction == CompositionStrategyPredictionState::SUCCESS) { - 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); - } + // Repaint the framebuffer (if needed), getting the optional fence for when + // the composition completes. + auto optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs); if (!optReadyFence) { return; } @@ -1079,8 +1004,16 @@ void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositi mRenderSurface->queueBuffer(std::move(*optReadyFence)); } -void Output::updateProtectedContentState() { +std::optional<base::unique_fd> Output::composeSurfaces( + const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + const auto& outputState = getState(); + OutputCompositionState& outputCompositionState = editState(); + const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition", + outputState.usesClientComposition}; + auto& renderEngine = getCompositionEngine().getRenderEngine(); const bool supportsProtectedContent = renderEngine.supportsProtectedContent(); @@ -1102,48 +1035,29 @@ void Output::updateProtectedContentState() { } else if (!outputState.isSecure && renderEngine.isProtected()) { renderEngine.useProtectedContext(false); } -} -bool Output::dequeueRenderBuffer(base::unique_fd* bufferFence, - std::shared_ptr<renderengine::ExternalTexture>* tex) { - const auto& outputState = getState(); + base::unique_fd fd; + + std::shared_ptr<renderengine::ExternalTexture> tex; // 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 (outputState.usesClientComposition || outputState.flipClientTarget) { - *tex = mRenderSurface->dequeueBuffer(bufferFence); - if (*tex == nullptr) { + if (hasClientComposition || outputState.flipClientTarget) { + tex = mRenderSurface->dequeueBuffer(&fd); + if (tex == nullptr) { ALOGW("Dequeuing buffer for display [%s] failed, bailing out of " "client composition for this frame", mName.c_str()); - return false; + return {}; } } - 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; @@ -1171,8 +1085,6 @@ 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, @@ -1180,19 +1092,16 @@ 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); } @@ -1203,8 +1112,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); } @@ -1440,13 +1353,12 @@ void Output::dirtyEntireOutput() { outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect()); } -std::optional<android::HWComposer::DeviceRequestedChanges> Output::chooseCompositionStrategy() { +void 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 { @@ -1461,63 +1373,5 @@ 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/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 7188281974..482250a165 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -18,19 +18,6 @@ #include <compositionengine/impl/OutputCompositionState.h> namespace android::compositionengine::impl { -using CompositionStrategyPredictionState = - OutputCompositionState::CompositionStrategyPredictionState; - -std::string toString(CompositionStrategyPredictionState state) { - switch (state) { - case CompositionStrategyPredictionState::DISABLED: - return "Disabled"; - case CompositionStrategyPredictionState::SUCCESS: - return "Success"; - case CompositionStrategyPredictionState::FAIL: - return "Fail"; - } -} void OutputCompositionState::dump(std::string& out) const { out.append(" "); @@ -69,7 +56,6 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "sdrWhitePointNits", sdrWhitePointNits); dumpVal(out, "clientTargetBrightness", clientTargetBrightness); dumpVal(out, "displayBrightness", displayBrightness); - dumpVal(out, "compositionStrategyPredictionState", toString(strategyPrediction)); out.append("\n"); } 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 5a3af7bfea..12c2c8eb38 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -289,9 +289,5 @@ 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/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index 2203f22263..aaca4fe424 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -20,6 +20,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/properties.h> +#include <android-base/stringprintf.h> #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/planner/CachedSet.h> #include <math/HashCombine.h> @@ -216,9 +217,8 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te renderengine::LayerSettings holePunchSettings; renderengine::LayerSettings holePunchBackgroundSettings; if (mHolePunchLayer) { - auto clientCompositionList = - mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList( - targetSettings); + auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE(); + auto clientCompositionList = layerFE.prepareClientCompositionList(targetSettings); // Assume that the final layer contains the buffer that we want to // replace with a hole punch. holePunchSettings = clientCompositionList.back(); @@ -227,7 +227,8 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f); holePunchSettings.disableBlending = true; holePunchSettings.alpha = 0.0f; - holePunchSettings.name = std::string("hole punch layer"); + holePunchSettings.name = + android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName()); layerSettings.push_back(holePunchSettings); // Add a solid background as the first layer in case there is no opaque @@ -306,7 +307,12 @@ bool CachedSet::requiresHolePunch() const { } const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE(); - if (layerFE.getCompositionState()->forceClientComposition) { + const auto* compositionState = layerFE.getCompositionState(); + if (compositionState->forceClientComposition) { + return false; + } + + if (compositionState->blendMode != hal::BlendMode::NONE) { return false; } @@ -390,7 +396,10 @@ void CachedSet::dump(std::string& result) const { const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr; base::StringAppendF(&result, " Override buffer: %p\n", b); } - base::StringAppendF(&result, " HolePunchLayer: %p\n", mHolePunchLayer); + base::StringAppendF(&result, " HolePunchLayer: %p\t%s\n", mHolePunchLayer, + mHolePunchLayer + ? mHolePunchLayer->getOutputLayer()->getLayerFE().getDebugName() + : ""); if (mLayers.size() == 1) { base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str()); diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 36b04d909f..cd0323555c 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -30,9 +30,7 @@ #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> @@ -200,22 +198,6 @@ 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 { @@ -232,11 +214,6 @@ 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; @@ -245,8 +222,6 @@ struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon { StrictMock<HWC2::mock::Layer> hwc2LayerUnknown; std::shared_ptr<Display> mDisplay = createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay()); - impl::GpuCompositionResult mResultWithBuffer; - impl::GpuCompositionResult mResultWithoutBuffer; }; /* @@ -579,7 +554,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfGpuDisplay) { createPartialMockDisplay<Display>(mCompositionEngine, args); EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId())); - chooseCompositionStrategy(gpuDisplay.get()); + gpuDisplay->chooseCompositionStrategy(); auto& state = gpuDisplay->getState(); EXPECT_TRUE(state.usesClientComposition); @@ -592,12 +567,11 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) { getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _)) .WillOnce(Return(INVALID_OPERATION)); - chooseCompositionStrategy(mDisplay.get()); + mDisplay->chooseCompositionStrategy(); auto& state = mDisplay->getState(); EXPECT_TRUE(state.usesClientComposition); EXPECT_FALSE(state.usesDeviceComposition); - EXPECT_FALSE(state.previousDeviceRequestedChanges.has_value()); } TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { @@ -614,16 +588,10 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _)) - .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); + .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); - chooseCompositionStrategy(mDisplay.get()); + mDisplay->chooseCompositionStrategy(); auto& state = mDisplay->getState(); EXPECT_FALSE(state.usesClientComposition); @@ -650,17 +618,11 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithDisplayBrightnes EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _)) - .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); + .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); mDisplay->setNextBrightness(kDisplayBrightness); - chooseCompositionStrategy(mDisplay.get()); + mDisplay->chooseCompositionStrategy(); auto& state = mDisplay->getState(); EXPECT_FALSE(state.usesClientComposition); @@ -669,6 +631,14 @@ 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. @@ -682,15 +652,13 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _)) - .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); + .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); EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); - chooseCompositionStrategy(mDisplay.get()); + mDisplay->chooseCompositionStrategy(); auto& state = mDisplay->getState(); EXPECT_FALSE(state.usesClientComposition); @@ -954,7 +922,7 @@ TEST_F(DisplayFinishFrameTest, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) { mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1)); mDisplay->editState().dirtyRegion = Region::INVALID_REGION; - mDisplay->finishFrame({}, std::move(mResultWithBuffer)); + mDisplay->finishFrame({}); } TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) { @@ -972,7 +940,7 @@ TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) { gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1)); gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION; - gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer)); + gpuDisplay->finishFrame({}); } TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) { @@ -989,7 +957,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({}, std::move(mResultWithBuffer)); + gpuDisplay->finishFrame({}); } /* diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 719f15cdc7..1f1dd1afb2 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -133,6 +133,8 @@ public: status_t(PhysicalDisplayId, std::optional<aidl::android::hardware::graphics::common:: DisplayDecorationSupport>* support)); + MOCK_METHOD2(setIdleTimerEnabled, status_t(PhysicalDisplayId, std::chrono::milliseconds)); + MOCK_METHOD1(hasDisplayIdleTimerCapability, bool(PhysicalDisplayId displayId)); }; } // namespace mock 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 0c5ea79350..54eb8f8465 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -28,7 +28,6 @@ #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> @@ -74,9 +73,6 @@ const mat4 kNonIdentityQuarter = mat4() * 0.25f; constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting = static_cast<OutputColorSetting>(0x100); -using CompositionStrategyPredictionState = android::compositionengine::impl:: - OutputCompositionState::CompositionStrategyPredictionState; - struct OutputPartialMockBase : public impl::Output { // compositionengine::Output overrides const OutputCompositionState& getState() const override { return mState; } @@ -993,7 +989,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, std::optional<DeviceRequestedChanges>()); + MOCK_METHOD0(chooseCompositionStrategy, void()); }; OutputPrepareFrameTest() { @@ -1024,7 +1020,6 @@ TEST_F(OutputPrepareFrameTest, delegatesToChooseCompositionStrategyAndRenderSurf EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)); mOutput.prepareFrame(); - EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::DISABLED); } // Note: Use OutputTest and not OutputPrepareFrameTest, so the real @@ -1040,134 +1035,6 @@ TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) { EXPECT_TRUE(mOutput->getState().usesClientComposition); EXPECT_FALSE(mOutput->getState().usesDeviceComposition); - EXPECT_EQ(mOutput->getState().strategyPrediction, CompositionStrategyPredictionState::DISABLED); -} - -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_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::SUCCESS); - 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_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL); - 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_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL); - 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_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL); - EXPECT_TRUE(result.bufferAvailable()); } /* @@ -1928,14 +1795,10 @@ 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_METHOD2(finishFrame, - void(const compositionengine::CompositionRefreshArgs&, - GpuCompositionResult&&)); + MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(postFramebuffer, void()); MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&)); - MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&)); }; StrictMock<OutputPartialMock> mOutput; @@ -1951,30 +1814,9 @@ 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, 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, finishFrame(Ref(args))); EXPECT_CALL(mOutput, postFramebuffer()); EXPECT_CALL(mOutput, renderCachedSets(Ref(args))); @@ -2872,15 +2714,11 @@ 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_METHOD4(composeSurfaces, + MOCK_METHOD2(composeSurfaces, std::optional<base::unique_fd>( - const Region&, const compositionengine::CompositionRefreshArgs&, - std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&)); + const Region&, const compositionengine::CompositionRefreshArgs&)); 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() { @@ -2937,9 +2775,7 @@ TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty InSequence seq; EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion)); - EXPECT_CALL(mOutput, updateProtectedContentState()); - EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)); - EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs), _, _)); + EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs))); EXPECT_CALL(*mRenderSurface, queueBuffer(_)); EXPECT_CALL(mOutput, postFramebuffer()); EXPECT_CALL(mOutput, prepareFrame()); @@ -2955,14 +2791,10 @@ 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_METHOD4(composeSurfaces, + MOCK_METHOD2(composeSurfaces, std::optional<base::unique_fd>( - const Region&, const compositionengine::CompositionRefreshArgs&, - std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&)); + const Region&, const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(postFramebuffer, void()); - MOCK_METHOD0(updateProtectedContentState, void()); - MOCK_METHOD2(dequeueRenderBuffer, - bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*)); }; OutputFinishFrameTest() { @@ -2980,62 +2812,27 @@ struct OutputFinishFrameTest : public testing::Test { TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) { mOutput.mState.isEnabled = false; - impl::GpuCompositionResult result; - mOutput.finishFrame(mRefreshArgs, std::move(result)); + mOutput.finishFrame(mRefreshArgs); } 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), _, _, _)); - - impl::GpuCompositionResult result; - mOutput.finishFrame(mRefreshArgs, std::move(result)); -} - -TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) { - mOutput.mState.isEnabled = true; - - InSequence seq; - 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(_)); - impl::GpuCompositionResult result; - mOutput.finishFrame(mRefreshArgs, std::move(result)); -} - -TEST_F(OutputFinishFrameTest, predictionSucceeded) { - mOutput.mState.isEnabled = true; - mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::SUCCESS; InSequence seq; - EXPECT_CALL(*mRenderSurface, queueBuffer(_)); + EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _)); - impl::GpuCompositionResult result; - mOutput.finishFrame(mRefreshArgs, std::move(result)); + mOutput.finishFrame(mRefreshArgs); } -TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) { +TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) { mOutput.mState.isEnabled = true; - mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::FAIL; InSequence seq; - - impl::GpuCompositionResult result; - 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)))) + EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _)) .WillOnce(Return(ByMove(base::unique_fd()))); EXPECT_CALL(*mRenderSurface, queueBuffer(_)); - mOutput.finishFrame(mRefreshArgs, std::move(result)); + + mOutput.finishFrame(mRefreshArgs); } /* @@ -3282,15 +3079,8 @@ struct OutputComposeSurfacesTest : public testing::Test { struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> { auto execute() { - 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); - } + getInstance()->mReadyFence = + getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); return nextState<FenceCheckState>(); } }; @@ -3851,11 +3641,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(mRenderEngine, useProtectedContext(false)); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) { @@ -3863,11 +3649,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotS mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) { @@ -3879,11 +3661,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLa EXPECT_CALL(mRenderEngine, useProtectedContext(false)); EXPECT_CALL(*mRenderSurface, setProtected(false)); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { @@ -3905,11 +3683,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) { @@ -3919,11 +3693,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) { @@ -3934,11 +3704,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRende EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); EXPECT_CALL(mRenderEngine, useProtectedContext(true)); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) { @@ -3949,11 +3715,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRend EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, setProtected(true)); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) { @@ -3964,11 +3726,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRend EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); EXPECT_CALL(mRenderEngine, useProtectedContext(true)); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest { @@ -3986,8 +3744,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; @@ -3997,11 +3756,7 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDatas .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur @@ -4036,12 +3791,7 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpen mOutput.writeCompositionState(mRefreshArgs); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, mRefreshArgs); } TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) { @@ -4051,12 +3801,7 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotEx mOutput.writeCompositionState(mRefreshArgs); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd); + mOutput.composeSurfaces(kDebugRegion, mRefreshArgs); } /* diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index 4ae921d661..8a99e4e2e8 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -539,6 +539,8 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { TEST_F(CachedSetTest, holePunch_requiresBuffer) { CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.blendMode = hal::BlendMode::NONE; sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; CachedSet cachedSet(layer1); @@ -549,7 +551,9 @@ TEST_F(CachedSetTest, holePunch_requiresBuffer) { TEST_F(CachedSetTest, holePunch_requiresRoundedCorners) { CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); - mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.blendMode = hal::BlendMode::NONE; CachedSet cachedSet(layer1); @@ -558,7 +562,9 @@ TEST_F(CachedSetTest, holePunch_requiresRoundedCorners) { TEST_F(CachedSetTest, holePunch_requiresSingleLayer) { CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); - mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.blendMode = hal::BlendMode::NONE; sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); @@ -575,7 +581,9 @@ TEST_F(CachedSetTest, holePunch_requiresNonHdr) { mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer); CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); - mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.blendMode = hal::BlendMode::NONE; sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; CachedSet cachedSet(layer); @@ -589,7 +597,22 @@ TEST_F(CachedSetTest, holePunch_requiresNonBT601_625) { mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer); CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); - mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.blendMode = hal::BlendMode::NONE; + sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; + + CachedSet cachedSet(layer); + EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + + EXPECT_FALSE(cachedSet.requiresHolePunch()); +} + +TEST_F(CachedSetTest, holePunch_requiresNoBlending) { + CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.blendMode = hal::BlendMode::PREMULTIPLIED; sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; CachedSet cachedSet(layer); @@ -600,7 +623,9 @@ TEST_F(CachedSetTest, holePunch_requiresNonBT601_625) { TEST_F(CachedSetTest, requiresHolePunch) { CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); - mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.blendMode = hal::BlendMode::NONE; sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; CachedSet cachedSet(layer); @@ -614,6 +639,7 @@ TEST_F(CachedSetTest, holePunch_requiresDeviceComposition) { sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.blendMode = hal::BlendMode::NONE; layerFECompositionState.forceClientComposition = true; CachedSet cachedSet(layer); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 656ef9abcb..96c28c9fc3 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -633,6 +633,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { auto& layerState3 = mTestLayers[2]->layerState; const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + mTestLayers[2]->layerFECompositionState.blendMode = hal::BlendMode::NONE; EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); @@ -706,6 +707,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { // a rounded updating layer auto& layerState1 = mTestLayers[1]->layerState; const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + mTestLayers[1]->layerFECompositionState.blendMode = hal::BlendMode::NONE; EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); @@ -777,6 +779,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { // a rounded updating layer auto& layerState1 = mTestLayers[1]->layerState; const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + mTestLayers[1]->layerFECompositionState.blendMode = hal::BlendMode::NONE; EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); 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 610d86f29a..45b98bb3d3 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -91,7 +91,6 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) static_cast<uint32_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers)); } - mCompositionDisplay->setPredictCompositionStrategy(mFlinger->mPredictCompositionStrategy); mCompositionDisplay->createDisplayColorProfile( compositionengine::DisplayColorProfileCreationArgsBuilder() .setHasWideColorGamut(args.hasWideColorGamut) diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 80e2d990d0..92592f7e09 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -248,6 +248,7 @@ bool AidlComposer::isSupported(OptionalFeature feature) const { case OptionalFeature::ExpectedPresentTime: case OptionalFeature::DisplayBrightnessCommand: case OptionalFeature::BootDisplayConfig: + case OptionalFeature::KernelIdleTimer: return true; } } @@ -475,6 +476,19 @@ Error AidlComposer::getDozeSupport(Display display, bool* outSupport) { return Error::NONE; } +Error AidlComposer::hasDisplayIdleTimerCapability(Display display, bool* outSupport) { + std::vector<AidlDisplayCapability> capabilities; + const auto status = + mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities); + if (!status.isOk()) { + ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + *outSupport = std::find(capabilities.begin(), capabilities.end(), + AidlDisplayCapability::DISPLAY_IDLE_TIMER) != capabilities.end(); + return Error::NONE; +} + Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) { @@ -1100,5 +1114,17 @@ Error AidlComposer::getDisplayDecorationSupport(Display display, } return Error::NONE; } + +Error AidlComposer::setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) { + const auto status = + mAidlComposerClient->setIdleTimerEnabled(translate<int64_t>(displayId), + translate<int32_t>(timeout.count())); + if (!status.isOk()) { + ALOGE("setIdleTimerEnabled failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + return Error::NONE; +} + } // namespace Hwc2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 80ca8da8dc..6c0f6362a0 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -100,6 +100,7 @@ public: std::vector<uint32_t>* outLayerRequestMasks) override; Error getDozeSupport(Display display, bool* outSupport) override; + Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override; Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) override; @@ -220,6 +221,7 @@ public: Error getPreferredBootDisplayConfig(Display displayId, Config*) override; Error getDisplayDecorationSupport(Display display, std::optional<DisplayDecorationSupport>* support) override; + Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 23886d4f3a..6abe7d14e2 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -93,6 +93,7 @@ public: // Whether setDisplayBrightness is able to be applied as part of a display command. DisplayBrightnessCommand, BootDisplayConfig, + KernelIdleTimer, }; virtual bool isSupported(OptionalFeature) const = 0; @@ -134,6 +135,7 @@ public: std::vector<uint32_t>* outLayerRequestMasks) = 0; virtual Error getDozeSupport(Display display, bool* outSupport) = 0; + virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0; virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) = 0; @@ -274,6 +276,7 @@ public: Display display, std::optional<::aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) = 0; + virtual Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index a1b663c0e7..650127668d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -148,6 +148,12 @@ bool Display::isVsyncPeriodSwitchSupported() const { return mComposer.isSupported(android::Hwc2::Composer::OptionalFeature::RefreshRateSwitching); } +bool Display::hasDisplayIdleTimerCapability() const { + bool isCapabilitySupported = false; + return mComposer.hasDisplayIdleTimerCapability(mId, &isCapabilitySupported) == Error::NONE && + isCapabilitySupported; +} + Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) { std::vector<Hwc2::Layer> layerIds; std::vector<Composition> types; @@ -588,6 +594,11 @@ Error Display::getDisplayDecorationSupport( return static_cast<Error>(error); } +Error Display::setIdleTimerEnabled(std::chrono::milliseconds timeout) { + const auto error = mComposer.setIdleTimerEnabled(mId, timeout); + return static_cast<Error>(error); +} + // For use by Device void Display::setConnected(bool connected) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 334d6ecbff..c03cede124 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -91,6 +91,7 @@ public: virtual bool hasCapability( aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0; virtual bool isVsyncPeriodSwitchSupported() const = 0; + virtual bool hasDisplayIdleTimerCapability() const = 0; virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0; [[clang::warn_unused_result]] virtual hal::Error acceptChanges() = 0; @@ -166,6 +167,8 @@ public: [[clang::warn_unused_result]] virtual hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) = 0; + [[clang::warn_unused_result]] virtual hal::Error setIdleTimerEnabled( + std::chrono::milliseconds timeout) = 0; }; namespace impl { @@ -242,6 +245,7 @@ public: hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; + hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override; // Other Display methods hal::HWDisplayId getId() const override { return mId; } @@ -250,6 +254,7 @@ public: bool hasCapability(aidl::android::hardware::graphics::composer3::DisplayCapability) const override EXCLUDES(mDisplayCapabilitiesMutex); bool isVsyncPeriodSwitchSupported() const override; + bool hasDisplayIdleTimerCapability() const override; void onLayerDestroyed(hal::HWLayerId layerId) override; private: diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index ed6e4b016c..02b3772baa 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -971,6 +971,26 @@ void HWComposer::loadCapabilities() { } } +status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId, + std::chrono::milliseconds timeout) { + ATRACE_CALL(); + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->setIdleTimerEnabled(timeout); + if (error == hal::Error::UNSUPPORTED) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + if (error == hal::Error::BAD_PARAMETER) { + RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); + } + RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); + return NO_ERROR; +} + +bool HWComposer::hasDisplayIdleTimerCapability(PhysicalDisplayId displayId) { + RETURN_IF_INVALID_DISPLAY(displayId, false); + return mDisplayData[displayId].hwcDisplay->hasDisplayIdleTimerCapability(); +} + void HWComposer::loadLayerMetadataSupport() { mSupportedLayerGenericMetadata.clear(); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index a8d439b7e0..f9637f0772 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -267,16 +267,10 @@ public: PhysicalDisplayId, std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) = 0; + virtual status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) = 0; + virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) = 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 { @@ -410,6 +404,8 @@ public: PhysicalDisplayId, std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; + status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) override; + bool hasDisplayIdleTimerCapability(PhysicalDisplayId) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index f735bbaec0..33adcebc1a 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -236,6 +236,7 @@ bool HidlComposer::isSupported(OptionalFeature feature) const { case OptionalFeature::ExpectedPresentTime: case OptionalFeature::DisplayBrightnessCommand: case OptionalFeature::BootDisplayConfig: + case OptionalFeature::KernelIdleTimer: return false; } } @@ -487,6 +488,11 @@ Error HidlComposer::getDozeSupport(Display display, bool* outSupport) { return error; } +Error HidlComposer::hasDisplayIdleTimerCapability(Display, bool*) { + LOG_ALWAYS_FATAL("hasDisplayIdleTimerCapability should have never been called on this as " + "OptionalFeature::KernelIdleTimer is not supported on HIDL"); +} + Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) { @@ -1320,6 +1326,11 @@ Error HidlComposer::getDisplayDecorationSupport( return Error::UNSUPPORTED; } +Error HidlComposer::setIdleTimerEnabled(Display, std::chrono::milliseconds) { + LOG_ALWAYS_FATAL("setIdleTimerEnabled should have never been called on this as " + "OptionalFeature::KernelIdleTimer is not supported on HIDL"); +} + void HidlComposer::registerCallback(ComposerCallback& callback) { const bool vsyncSwitchingSupported = isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching); diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index c2b60cb6de..a1ea4f2e13 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -208,6 +208,7 @@ public: std::vector<uint32_t>* outLayerRequestMasks) override; Error getDozeSupport(Display display, bool* outSupport) override; + Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override; Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) override; @@ -331,6 +332,7 @@ public: Display display, std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; + Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index e21095aa88..307da41667 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -89,9 +89,6 @@ 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/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 81747d596a..ec59888508 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -618,15 +618,15 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, } } -void SurfaceFrame::tracePredictions(int64_t displayFrameToken) const { +void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime)); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start(); @@ -644,8 +644,8 @@ void SurfaceFrame::tracePredictions(int64_t displayFrameToken) const { FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime)); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedSurfaceFrameEndEvent = event->set_frame_end(); @@ -654,14 +654,14 @@ void SurfaceFrame::tracePredictions(int64_t displayFrameToken) const { }); } -void SurfaceFrame::traceActuals(int64_t displayFrameToken) const { +void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); // Actual start time is not yet available, so use expected start instead if (mPredictionState == PredictionState::Expired) { // If prediction is expired, we can't use the predicted start time. Instead, just use a @@ -669,11 +669,12 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken) const { // frame in the trace. nsecs_t endTime = (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime); - packet->set_timestamp( - static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta)); + packet->set_timestamp(static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta + + monoBootOffset)); } else { - packet->set_timestamp(static_cast<uint64_t>( - mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime)); + packet->set_timestamp(static_cast<uint64_t>(monoBootOffset + mActuals.startTime == 0 + ? mPredictions.startTime + : mActuals.startTime)); } auto* event = packet->set_frame_timeline_event(); @@ -706,11 +707,11 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken) const { FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); if (mPresentState == PresentState::Dropped) { - packet->set_timestamp(static_cast<uint64_t>(mDropTime)); + packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset)); } else { - packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime)); + packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset)); } auto* event = packet->set_frame_timeline_event(); @@ -723,7 +724,7 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken) const { /** * TODO(b/178637512): add inputEventId to the perfetto trace. */ -void SurfaceFrame::trace(int64_t displayFrameToken) const { +void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const { if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID || displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) { // No packets can be traced with a missing token. @@ -732,9 +733,9 @@ void SurfaceFrame::trace(int64_t displayFrameToken) const { if (getPredictionState() != PredictionState::Expired) { // Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in // a trace. - tracePredictions(displayFrameToken); + tracePredictions(displayFrameToken, monoBootOffset); } - traceActuals(displayFrameToken); + traceActuals(displayFrameToken, monoBootOffset); } namespace impl { @@ -760,8 +761,9 @@ std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) } FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, - JankClassificationThresholds thresholds) - : mMaxDisplayFrames(kDefaultMaxDisplayFrames), + JankClassificationThresholds thresholds, bool useBootTimeClock) + : mUseBootTimeClock(useBootTimeClock), + mMaxDisplayFrames(kDefaultMaxDisplayFrames), mTimeStats(std::move(timeStats)), mSurfaceFlingerPid(surfaceFlingerPid), mJankClassificationThresholds(thresholds) { @@ -1016,14 +1018,16 @@ void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previous } } -void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid) const { +void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, + nsecs_t monoBootOffset) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime)); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start(); @@ -1037,8 +1041,9 @@ void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid) cons // Expected timeline end FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime)); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedDisplayFrameEndEvent = event->set_frame_end(); @@ -1047,14 +1052,16 @@ void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid) cons }); } -void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid) const { +void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, + nsecs_t monoBootOffset) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.startTime)); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerActuals.startTime + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start(); @@ -1075,8 +1082,9 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid) const { // Actual timeline end FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime)); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* actualDisplayFrameEndEvent = event->set_frame_end(); @@ -1085,7 +1093,7 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid) const { }); } -void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { +void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const { if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) { // DisplayFrame should not have an invalid token. ALOGE("Cannot trace DisplayFrame with invalid token"); @@ -1095,12 +1103,12 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { if (mPredictionState == PredictionState::Valid) { // Expired and unknown predictions have zeroed timestamps. This cannot be used in any // meaningful way in a trace. - tracePredictions(surfaceFlingerPid); + tracePredictions(surfaceFlingerPid, monoBootOffset); } - traceActuals(surfaceFlingerPid); + traceActuals(surfaceFlingerPid, monoBootOffset); for (auto& surfaceFrame : mSurfaceFrames) { - surfaceFrame->trace(mToken); + surfaceFrame->trace(mToken, monoBootOffset); } } @@ -1164,6 +1172,12 @@ float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) { } void FrameTimeline::flushPendingPresentFences() { + // Perfetto is using boottime clock to void drifts when the device goes + // to suspend. + const auto monoBootOffset = mUseBootTimeClock + ? (systemTime(SYSTEM_TIME_BOOTTIME) - systemTime(SYSTEM_TIME_MONOTONIC)) + : 0; + for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& pendingPresentFence = mPendingPresentFences[i]; nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; @@ -1175,7 +1189,7 @@ void FrameTimeline::flushPendingPresentFences() { } auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousPresentTime); - displayFrame->trace(mSurfaceFlingerPid); + displayFrame->trace(mSurfaceFlingerPid, monoBootOffset); mPreviousPresentTime = signalTime; mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 36d629077d..a2305af554 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -204,8 +204,9 @@ public: std::string miniDump() const; // Emits a packet for perfetto tracing. The function body will be executed only if tracing is // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding - // DisplayFrame at the trace processor side. - void trace(int64_t displayFrameToken) const; + // DisplayFrame at the trace processor side. monoBootOffset is the difference + // between SYSTEM_TIME_BOOTTIME and SYSTEM_TIME_MONOTONIC. + void trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const; // Getter functions used only by FrameTimelineTests and SurfaceFrame internally TimelineItem getActuals() const; @@ -225,8 +226,8 @@ public: std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); private: - void tracePredictions(int64_t displayFrameToken) const; - void traceActuals(int64_t displayFrameToken) const; + void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const; + void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const; void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, nsecs_t& deadlineDelta) REQUIRES(mMutex); @@ -369,8 +370,9 @@ public: // Dumpsys interface - dumps all data irrespective of jank void dumpAll(std::string& result, nsecs_t baseTime) const; // Emits a packet for perfetto tracing. The function body will be executed only if tracing - // is enabled. - void trace(pid_t surfaceFlingerPid) const; + // is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME + // and SYSTEM_TIME_MONOTONIC. + void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const; // Sets the token, vsyncPeriod, predictions and SF start time. void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime); @@ -401,8 +403,8 @@ public: private: void dump(std::string& result, nsecs_t baseTime) const; - void tracePredictions(pid_t surfaceFlingerPid) const; - void traceActuals(pid_t surfaceFlingerPid) const; + void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const; + void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const; void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync, nsecs_t previousPresentTime); @@ -442,7 +444,7 @@ public: }; FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, - JankClassificationThresholds thresholds = {}); + JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true); ~FrameTimeline() = default; frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } @@ -484,6 +486,7 @@ private: TokenManager mTokenManager; TraceCookieCounter mTraceCookieCounter; mutable std::mutex mMutex; + const bool mUseBootTimeClock; uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; const pid_t mSurfaceFlingerPid; diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2b5e337095..48a9bc50c4 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -1,4 +1,3 @@ - /* * Copyright (C) 2007 The Android Open Source Project * @@ -1049,7 +1048,7 @@ protected: mutable bool mDrawingStateModified = false; sp<Fence> mLastClientCompositionFence; - bool mLastClientCompositionDisplayed = false; + bool mAlreadyDisplayedThisCompose = false; private: virtual void setTransformHint(ui::Transform::RotationFlags) {} diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h index 6a906944a7..41273e01c1 100644 --- a/services/surfaceflinger/LayerRenderArea.h +++ b/services/surfaceflinger/LayerRenderArea.h @@ -46,6 +46,7 @@ public: Rect getSourceCrop() const override; void render(std::function<void()> drawLayers) override; + virtual sp<Layer> getParentLayer() const { return mLayer; } private: const sp<Layer> mLayer; @@ -58,4 +59,4 @@ private: const bool mChildrenOnly; }; -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index c9f7f46953..387364c03a 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -4,6 +4,7 @@ #include <ui/Transform.h> #include <functional> +#include "Layer.h" namespace android { @@ -85,6 +86,10 @@ public: // Returns the source display viewport. const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; } + // If this is a LayerRenderArea, return the root layer of the + // capture operation. + virtual sp<Layer> getParentLayer() const { return nullptr; } + protected: const bool mAllowSecureLayers; 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/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 15e30b3c59..65c8613c1b 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -670,9 +670,9 @@ RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId } void RefreshRateConfigs::initializeIdleTimer() { - if (mConfig.idleTimerTimeoutMs > 0) { + if (mConfig.idleTimerTimeout > 0ms) { mIdleTimer.emplace( - "IdleTimer", std::chrono::milliseconds(mConfig.idleTimerTimeoutMs), + "IdleTimer", mConfig.idleTimerTimeout, [this] { std::scoped_lock lock(mIdleTimerCallbacksMutex); if (const auto callbacks = getIdleTimerCallbacks()) { @@ -963,12 +963,24 @@ void RefreshRateConfigs::dump(std::string& result) const { base::StringAppendF(&result, "Supports Frame Rate Override By Content: %s\n", mSupportsFrameRateOverrideByContent ? "yes" : "no"); - base::StringAppendF(&result, "Idle timer: (%s) %s\n", - mConfig.supportKernelIdleTimer ? "kernel" : "platform", - mIdleTimer ? mIdleTimer->dump().c_str() : "off"); + base::StringAppendF(&result, "Idle timer: "); + if (mConfig.kernelIdleTimerController.has_value()) { + if (mConfig.kernelIdleTimerController == KernelIdleTimerController::Sysprop) { + base::StringAppendF(&result, "(kernel(sysprop))"); + } else { + base::StringAppendF(&result, "(kernel(hwc))"); + } + } else { + base::StringAppendF(&result, "(platform)"); + } + base::StringAppendF(&result, " %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off"); result.append("\n"); } +std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() { + return mConfig.idleTimerTimeout; +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 14583e3617..30d3edd05e 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -272,6 +272,8 @@ public: // Returns a known frame rate that is the closest to frameRate Fps findClosestKnownFrameRate(Fps frameRate) const; + enum class KernelIdleTimerController { Sysprop, HwcApi }; + // Configuration flags. struct Config { bool enableFrameRateOverride = false; @@ -282,17 +284,18 @@ public: int frameRateMultipleThreshold = 0; // The Idle Timer timeout. 0 timeout means no idle timer. - int32_t idleTimerTimeoutMs = 0; + std::chrono::milliseconds idleTimerTimeout = 0ms; - // Whether to use idle timer callbacks that support the kernel timer. - bool supportKernelIdleTimer = false; + // The controller representing how the kernel idle timer will be configured + // either on the HWC api or sysprop. + std::optional<KernelIdleTimerController> kernelIdleTimerController; }; RefreshRateConfigs(const DisplayModes&, DisplayModeId, Config config = {.enableFrameRateOverride = false, .frameRateMultipleThreshold = 0, - .idleTimerTimeoutMs = 0, - .supportKernelIdleTimer = false}); + .idleTimerTimeout = 0ms, + .kernelIdleTimerController = {}}); RefreshRateConfigs(const RefreshRateConfigs&) = delete; RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete; @@ -310,6 +313,7 @@ public: TurnOff, // Turn off the idle timer. TurnOn // Turn on the idle timer. }; + // Checks whether kernel idle timer should be active depending the policy decisions around // refresh rates. KernelIdleTimerAction getIdleTimerAction() const; @@ -332,7 +336,9 @@ public: Fps displayFrameRate, GlobalSignals) const EXCLUDES(mLock); - bool supportsKernelIdleTimer() const { return mConfig.supportKernelIdleTimer; } + std::optional<KernelIdleTimerController> kernelIdleTimerController() { + return mConfig.kernelIdleTimerController; + } struct IdleTimerCallbacks { struct Callbacks { @@ -370,7 +376,7 @@ public: if (!mIdleTimer) { return; } - if (kernelOnly && !mConfig.supportKernelIdleTimer) { + if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) { return; } mIdleTimer->reset(); @@ -378,6 +384,8 @@ public: void dump(std::string& result) const EXCLUDES(mLock); + std::chrono::milliseconds getIdleTimerTimeout(); + private: friend struct TestableRefreshRateConfigs; @@ -437,8 +445,8 @@ private: std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const REQUIRES(mIdleTimerCallbacksMutex) { if (!mIdleTimerCallbacks) return {}; - return mConfig.supportKernelIdleTimer ? mIdleTimerCallbacks->kernel - : mIdleTimerCallbacks->platform; + return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel + : mIdleTimerCallbacks->platform; } // The list of refresh rates, indexed by display modes ID. This may change after this diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 350d7096aa..268036ce0c 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -169,8 +169,8 @@ using aidl::android::hardware::graphics::common::DisplayDecorationSupport; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; -using CompositionStrategyPredictionState = android::compositionengine::impl:: - OutputCompositionState::CompositionStrategyPredictionState; +using KernelIdleTimerController = + ::android::scheduler::RefreshRateConfigs::KernelIdleTimerController; namespace android { @@ -272,39 +272,33 @@ bool validateCompositionDataspace(Dataspace dataspace) { return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3; } - -struct IdleTimerConfig { - int32_t timeoutMs; - bool supportKernelIdleTimer; -}; - -IdleTimerConfig getIdleTimerConfiguration(DisplayId displayId) { - // TODO(adyabr): use ro.surface_flinger.* namespace - +std::chrono::milliseconds getIdleTimerTimeout(DisplayId displayId) { const auto displayIdleTimerMsKey = [displayId] { std::stringstream ss; ss << "debug.sf.set_idle_timer_ms_" << displayId.value; return ss.str(); }(); + const int32_t displayIdleTimerMs = base::GetIntProperty(displayIdleTimerMsKey, 0); + if (displayIdleTimerMs > 0) { + return std::chrono::milliseconds(displayIdleTimerMs); + } + + const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms", 0); + const int32_t millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0); + return std::chrono::milliseconds(millis); +} + +bool getKernelIdleTimerSyspropConfig(DisplayId displayId) { const auto displaySupportKernelIdleTimerKey = [displayId] { std::stringstream ss; ss << "debug.sf.support_kernel_idle_timer_" << displayId.value; return ss.str(); }(); - const int32_t displayIdleTimerMs = base::GetIntProperty(displayIdleTimerMsKey, 0); const auto displaySupportKernelIdleTimer = base::GetBoolProperty(displaySupportKernelIdleTimerKey, false); - - if (displayIdleTimerMs > 0) { - return {displayIdleTimerMs, displaySupportKernelIdleTimer}; - } - - const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms", 0); - const int32_t millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0); - - return {millis, sysprop::support_kernel_idle_timer(false)}; + return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false); } } // namespace anonymous @@ -482,9 +476,6 @@ 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, "1"); - 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 @@ -2199,8 +2190,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(); @@ -2272,24 +2263,24 @@ void SurfaceFlinger::composite(nsecs_t frameTime) { const bool prevFrameHadClientComposition = mHadClientComposition; - mHadClientComposition = mHadDeviceComposition = mReusedClientComposition = false; - TimeStats::ClientCompositionRecord clientCompositionRecord; - for (const auto& [_, display] : displays) { - const auto& state = display->getCompositionDisplay()->getState(); - mHadClientComposition |= state.usesClientComposition && !state.reusedClientComposition; - mHadDeviceComposition |= state.usesDeviceComposition; - mReusedClientComposition |= state.reusedClientComposition; - clientCompositionRecord.predicted |= - (state.strategyPrediction != CompositionStrategyPredictionState::DISABLED); - clientCompositionRecord.predictionSucceeded |= - (state.strategyPrediction == CompositionStrategyPredictionState::SUCCESS); + mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) { + const auto& state = pair.second->getCompositionDisplay()->getState(); + return state.usesClientComposition && !state.reusedClientComposition; + }); + mHadDeviceComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) { + const auto& state = pair.second->getCompositionDisplay()->getState(); + return state.usesDeviceComposition; + }); + mReusedClientComposition = + std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) { + const auto& state = pair.second->getCompositionDisplay()->getState(); + return state.reusedClientComposition; + }); + // Only report a strategy change if we move in and out of client composition + if (prevFrameHadClientComposition != mHadClientComposition) { + mTimeStats->incrementCompositionStrategyChanges(); } - clientCompositionRecord.hadClientComposition = mHadClientComposition; - clientCompositionRecord.reused = mReusedClientComposition; - clientCompositionRecord.changed = prevFrameHadClientComposition != mHadClientComposition; - mTimeStats->pushCompositionStrategyState(clientCompositionRecord); - // TODO: b/160583065 Enable skip validation when SF caches all client composition layers const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition; modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition); @@ -2544,6 +2535,13 @@ void SurfaceFlinger::postComposition() { } mTimeStats->incrementTotalFrames(); + if (mHadClientComposition) { + mTimeStats->incrementClientCompositionFrames(); + } + + if (mReusedClientComposition) { + mTimeStats->incrementClientCompositionReusedFrames(); + } mTimeStats->setPresentFenceGlobal(mPreviousPresentFences[0].fenceTime); @@ -2827,14 +2825,15 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( creationArgs.connectionType = physical->type; creationArgs.supportedModes = physical->supportedModes; creationArgs.activeModeId = physical->activeMode->getId(); - const auto [idleTimerTimeoutMs, supportKernelIdleTimer] = - getIdleTimerConfiguration(compositionDisplay->getId()); + const auto [kernelIdleTimerController, idleTimerTimeoutMs] = + getKernelIdleTimerProperties(compositionDisplay->getId()); + scheduler::RefreshRateConfigs::Config config = {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false), .frameRateMultipleThreshold = base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0), - .idleTimerTimeoutMs = idleTimerTimeoutMs, - .supportKernelIdleTimer = supportKernelIdleTimer}; + .idleTimerTimeout = idleTimerTimeoutMs, + .kernelIdleTimerController = kernelIdleTimerController}; creationArgs.refreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes, creationArgs.activeModeId, config); @@ -3408,7 +3407,7 @@ void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { features); { auto configs = display->holdRefreshRateConfigs(); - if (configs->supportsKernelIdleTimer()) { + if (configs->kernelIdleTimerController().has_value()) { features |= Feature::kKernelIdleTimer; } @@ -3674,11 +3673,12 @@ bool SurfaceFlinger::stopTransactionProcessing( return false; } -void SurfaceFlinger::flushPendingTransactionQueues( +int SurfaceFlinger::flushPendingTransactionQueues( std::vector<TransactionState>& transactions, - 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, bool tryApplyUnsignaled) { + int transactionsPendingBarrier = 0; auto it = mPendingTransactionQueues.begin(); while (it != mPendingTransactionQueues.end()) { auto& [applyToken, transactionQueue] = *it; @@ -3701,8 +3701,21 @@ void SurfaceFlinger::flushPendingTransactionQueues( setTransactionFlags(eTransactionFlushNeeded); break; } + if (ready == TransactionReadiness::NotReadyBarrier) { + transactionsPendingBarrier++; + setTransactionFlags(eTransactionFlushNeeded); + break; + } transaction.traverseStatesWithBuffers([&](const layer_state_t& state) { - bufferLayersReadyToPresent.insert(state.surface); + 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) { @@ -3720,6 +3733,7 @@ void SurfaceFlinger::flushPendingTransactionQueues( it = std::next(it, 1); } } + return transactionsPendingBarrier; } bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { @@ -3728,19 +3742,21 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { // 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); + 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. - flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent, - applyTokensWithUnsignaledTransactions, - /*tryApplyUnsignaled*/ false); + 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 @@ -3765,18 +3781,48 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { /*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 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. @@ -3892,7 +3938,8 @@ 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, + 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(); @@ -3930,6 +3977,17 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( continue; } + 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(), @@ -3950,8 +4008,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; @@ -6087,6 +6145,47 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { })); } +std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> +SurfaceFlinger::getKernelIdleTimerProperties(DisplayId displayId) { + const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported( + android::Hwc2::Composer::OptionalFeature::KernelIdleTimer); + const auto timeout = getIdleTimerTimeout(displayId); + if (isKernelIdleTimerHwcSupported) { + if (const auto id = PhysicalDisplayId::tryCast(displayId); + getHwComposer().hasDisplayIdleTimerCapability(*id)) { + // In order to decide if we can use the HWC api for idle timer + // we query DisplayCapability::DISPLAY_IDLE_TIMER directly on the composer + // without relying on hasDisplayCapability. + // hasDisplayCapability relies on DisplayCapabilities + // which are updated after we set the PowerMode::ON. + // DISPLAY_IDLE_TIMER is a display driver property + // and is available before the PowerMode::ON + return {KernelIdleTimerController::HwcApi, timeout}; + } + return {std::nullopt, timeout}; + } + if (getKernelIdleTimerSyspropConfig(displayId)) { + return {KernelIdleTimerController::Sysprop, timeout}; + } + + return {std::nullopt, timeout}; +} + +void SurfaceFlinger::updateKernelIdleTimer(std::chrono::milliseconds timeout, + KernelIdleTimerController controller, + PhysicalDisplayId displayId) { + switch (controller) { + case KernelIdleTimerController::HwcApi: { + getHwComposer().setIdleTimerEnabled(displayId, timeout); + break; + } + case KernelIdleTimerController::Sysprop: { + base::SetProperty(KERNEL_IDLE_TIMER_PROP, timeout > 0ms ? "true" : "false"); + break; + } + } +} + void SurfaceFlinger::toggleKernelIdleTimer() { using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction; @@ -6098,23 +6197,31 @@ void SurfaceFlinger::toggleKernelIdleTimer() { // If the support for kernel idle timer is disabled for the active display, // don't do anything. - if (!display->refreshRateConfigs().supportsKernelIdleTimer()) { + const std::optional<KernelIdleTimerController> kernelIdleTimerController = + display->refreshRateConfigs().kernelIdleTimerController(); + if (!kernelIdleTimerController.has_value()) { return; } const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction(); + switch (action) { case KernelIdleTimerAction::TurnOff: if (mKernelIdleTimerEnabled) { ATRACE_INT("KernelIdleTimer", 0); - base::SetProperty(KERNEL_IDLE_TIMER_PROP, "false"); + std::chrono::milliseconds constexpr kTimerDisabledTimeout = 0ms; + updateKernelIdleTimer(kTimerDisabledTimeout, kernelIdleTimerController.value(), + display->getPhysicalId()); mKernelIdleTimerEnabled = false; } break; case KernelIdleTimerAction::TurnOn: if (!mKernelIdleTimerEnabled) { ATRACE_INT("KernelIdleTimer", 1); - base::SetProperty(KERNEL_IDLE_TIMER_PROP, "true"); + const std::chrono::milliseconds timeout = + display->refreshRateConfigs().getIdleTimerTimeout(); + updateKernelIdleTimer(timeout, kernelIdleTimerController.value(), + display->getPhysicalId()); mKernelIdleTimerEnabled = true; } break; @@ -6392,19 +6499,6 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, // and failed if display is not in native mode. This provide a way to force using native // colors when capture. dataspace = args.dataspace; - if (dataspace == ui::Dataspace::UNKNOWN) { - auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) { - return display.getLayerStack() == layerStack; - }); - if (!display) { - // If the layer is not on a display, use the dataspace for the default display. - display = getDefaultDisplayDeviceLocked(); - } - - const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode; - dataspace = pickDataspaceFromColorMode(colorMode); - } - } // mStateLock // really small crop or frameScale @@ -6533,7 +6627,7 @@ std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScre renderArea->render([&] { renderEngineResultFuture = - renderScreenImplLocked(*renderArea, traverseLayers, buffer, + renderScreenImpl(*renderArea, traverseLayers, buffer, canCaptureBlackoutContent, regionSampling, grayscale, captureResults); }); @@ -6566,7 +6660,7 @@ std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScre } } -std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScreenImplLocked( +std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScreenImpl( const RenderArea& renderArea, TraverseLayersFunction traverseLayers, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool canCaptureBlackoutContent, bool regionSampling, bool grayscale, @@ -6590,7 +6684,22 @@ std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScree } captureResults.buffer = buffer->getBuffer(); - captureResults.capturedDataspace = renderArea.getReqDataSpace(); + auto dataspace = renderArea.getReqDataSpace(); + auto parent = renderArea.getParentLayer(); + if ((dataspace == ui::Dataspace::UNKNOWN) && (parent != nullptr)) { + Mutex::Autolock lock(mStateLock); + auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) { + return display.getLayerStack() == layerStack; + }); + if (!display) { + // If the layer is not on a display, use the dataspace for the default display. + display = getDefaultDisplayDeviceLocked(); + } + + const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode; + dataspace = pickDataspaceFromColorMode(colorMode); + } + captureResults.capturedDataspace = dataspace; const auto reqWidth = renderArea.getReqWidth(); const auto reqHeight = renderArea.getReqHeight(); @@ -6608,7 +6717,7 @@ std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScree clientCompositionDisplay.clip = sourceCrop; clientCompositionDisplay.orientation = rotation; - clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace(); + clientCompositionDisplay.outputDataspace = dataspace; clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance; const float colorSaturation = grayscale ? 0 : 1; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c852c07638..81afa9b0fa 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -344,11 +344,6 @@ 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(); @@ -468,6 +463,8 @@ private: }; using ActiveModeInfo = DisplayDevice::ActiveModeInfo; + using KernelIdleTimerController = + ::android::scheduler::RefreshRateConfigs::KernelIdleTimerController; enum class BootStage { BOOTLOADER, @@ -672,7 +669,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; @@ -691,6 +688,14 @@ private: void triggerOnFrameRateOverridesChanged() override; // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); + // Get the controller and timeout that will help decide how the kernel idle timer will be + // configured and what value to use as the timeout. + std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> + getKernelIdleTimerProperties(DisplayId) REQUIRES(mStateLock); + // Updates the kernel idle timer either through HWC or through sysprop + // depending on which controller is provided + void updateKernelIdleTimer(std::chrono::milliseconds timeoutMs, KernelIdleTimerController, + PhysicalDisplayId) REQUIRES(mStateLock); // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to // make calls to sys prop each time. bool mKernelIdleTimerEnabled = false; @@ -761,9 +766,9 @@ private: // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); - void flushPendingTransactionQueues( + int flushPendingTransactionQueues( std::vector<TransactionState>& transactions, - 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, bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock); @@ -790,13 +795,15 @@ 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, + 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, @@ -857,10 +864,10 @@ private: RenderAreaFuture, TraverseLayersFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, const sp<IScreenCaptureListener>&); - std::shared_future<renderengine::RenderEngineResult> renderScreenImplLocked( + std::shared_future<renderengine::RenderEngineResult> renderScreenImpl( const RenderArea&, TraverseLayersFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent, - bool regionSampling, bool grayscale, ScreenCaptureResults&); + bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock); // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a // matching ownerUid diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index e5a9dd47c3..b1a2bdaa91 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -321,19 +321,22 @@ void TimeStats::incrementMissedFrames() { mTimeStats.missedFramesLegacy++; } -void TimeStats::pushCompositionStrategyState(const TimeStats::ClientCompositionRecord& record) { - if (!mEnabled.load() || !record.hasInterestingData()) { - return; - } +void TimeStats::incrementClientCompositionFrames() { + if (!mEnabled.load()) return; + + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mMutex); + mTimeStats.clientCompositionFramesLegacy++; +} + +void TimeStats::incrementClientCompositionReusedFrames() { + if (!mEnabled.load()) return; ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - if (record.changed) mTimeStats.compositionStrategyChangesLegacy++; - if (record.hadClientComposition) mTimeStats.clientCompositionFramesLegacy++; - if (record.reused) mTimeStats.clientCompositionReusedFramesLegacy++; - if (record.predicted) mTimeStats.compositionStrategyPredictedLegacy++; - if (record.predictionSucceeded) mTimeStats.compositionStrategyPredictionSucceededLegacy++; + mTimeStats.clientCompositionReusedFramesLegacy++; } void TimeStats::incrementRefreshRateSwitches() { @@ -345,6 +348,15 @@ void TimeStats::incrementRefreshRateSwitches() { mTimeStats.refreshRateSwitchesLegacy++; } +void TimeStats::incrementCompositionStrategyChanges() { + if (!mEnabled.load()) return; + + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mMutex); + mTimeStats.compositionStrategyChangesLegacy++; +} + void TimeStats::recordDisplayEventConnectionCount(int32_t count) { if (!mEnabled.load()) return; @@ -1050,10 +1062,8 @@ void TimeStats::clearGlobalLocked() { mTimeStats.missedFramesLegacy = 0; mTimeStats.clientCompositionFramesLegacy = 0; mTimeStats.clientCompositionReusedFramesLegacy = 0; - mTimeStats.compositionStrategyChangesLegacy = 0; - mTimeStats.compositionStrategyPredictedLegacy = 0; - mTimeStats.compositionStrategyPredictionSucceededLegacy = 0; mTimeStats.refreshRateSwitchesLegacy = 0; + mTimeStats.compositionStrategyChangesLegacy = 0; mTimeStats.displayEventConnectionsCountLegacy = 0; mTimeStats.displayOnTimeLegacy = 0; mTimeStats.presentToPresentLegacy.hist.clear(); diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 7a159b8eb7..77c7973532 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -45,7 +45,7 @@ public: virtual ~TimeStats() = default; // Process a pull request from statsd. - virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0; + virtual bool onPullAtom(const int atomId, std::string* pulledData); virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0; virtual bool isEnabled() = 0; @@ -53,8 +53,14 @@ public: virtual void incrementTotalFrames() = 0; virtual void incrementMissedFrames() = 0; + virtual void incrementClientCompositionFrames() = 0; + virtual void incrementClientCompositionReusedFrames() = 0; // Increments the number of times the display refresh rate changed. virtual void incrementRefreshRateSwitches() = 0; + // Increments the number of changes in composition strategy + // The intention is to reflect the number of changes between hwc and gpu + // composition, where "gpu composition" may also include mixed composition. + virtual void incrementCompositionStrategyChanges() = 0; // Records the most up-to-date count of display event connections. // The stored count will be the maximum ever recoded. virtual void recordDisplayEventConnectionCount(int32_t count) = 0; @@ -152,24 +158,6 @@ public: } }; - struct ClientCompositionRecord { - // Frame had client composition or mixed composition - bool hadClientComposition = false; - // Composition changed between hw composition and mixed/client composition - bool changed = false; - // Frame reused the client composition result from a previous frame - bool reused = false; - // Composition strategy predicted for frame - bool predicted = false; - // Composition strategy prediction succeeded - bool predictionSucceeded = false; - - // Whether there is data we want to record. - bool hasInterestingData() const { - return hadClientComposition || changed || reused || predicted; - } - }; - virtual void incrementJankyFrames(const JankyFramesInfo& info) = 0; // Clean up the layer record virtual void onDestroy(int32_t layerId) = 0; @@ -181,7 +169,6 @@ public: // Source of truth is RefrehRateStats. virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0; virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0; - virtual void pushCompositionStrategyState(const ClientCompositionRecord&) = 0; }; namespace impl { @@ -249,7 +236,10 @@ public: void incrementTotalFrames() override; void incrementMissedFrames() override; + void incrementClientCompositionFrames() override; + void incrementClientCompositionReusedFrames() override; void incrementRefreshRateSwitches() override; + void incrementCompositionStrategyChanges() override; void recordDisplayEventConnectionCount(int32_t count) override; void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override; @@ -285,8 +275,6 @@ public: void recordRefreshRate(uint32_t fps, nsecs_t duration) override; void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override; - void pushCompositionStrategyState(const ClientCompositionRecord&) override; - static const size_t MAX_NUM_TIME_RECORDS = 64; private: diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index cf1ca65972..69afa2a7a1 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -143,14 +143,6 @@ std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> m clientCompositionReusedFramesLegacy); StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitchesLegacy); StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChangesLegacy); - StringAppendF(&result, "compositionStrategyPredicted = %d\n", - compositionStrategyPredictedLegacy); - StringAppendF(&result, "compositionStrategyPredictionSucceeded = %d\n", - compositionStrategyPredictionSucceededLegacy); - StringAppendF(&result, "compositionStrategyPredictionFailed = %d\n", - compositionStrategyPredictedLegacy - - compositionStrategyPredictionSucceededLegacy); - StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTimeLegacy); StringAppendF(&result, "displayConfigStats is as below:\n"); for (const auto& [fps, duration] : refreshRateStatsLegacy) { diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 237ae8d761..438561cc05 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -178,8 +178,6 @@ public: Histogram frameDurationLegacy; Histogram renderEngineTimingLegacy; std::unordered_map<uint32_t, nsecs_t> refreshRateStatsLegacy; - int32_t compositionStrategyPredictedLegacy = 0; - int32_t compositionStrategyPredictionSucceededLegacy = 0; std::unordered_map<TimelineStatsKey, TimelineStats, TimelineStatsKey::Hasher> stats; 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/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/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/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 16690757e9..15c9d199cd 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -244,8 +244,8 @@ void CompositionTest::captureScreenComposition() { HAL_PIXEL_FORMAT_RGBA_8888, 1, usage); - auto result = mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer, - forSystem, regionSampling); + auto result = mFlinger.renderScreenImpl(*renderArea, traverseLayers, mCaptureScreenBuffer, + forSystem, regionSampling); EXPECT_TRUE(result.valid()); auto& [status, drawFence] = result.get(); diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 834a560b3b..f1efa9286d 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -66,9 +66,10 @@ public: } void SetUp() override { + constexpr bool kUseBootTimeClock = true; mTimeStats = std::make_shared<mock::TimeStats>(); mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid, - kTestThresholds); + kTestThresholds, !kUseBootTimeClock); mFrameTimeline->registerDataSource(); mTokenManager = &mFrameTimeline->mTokenManager; mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter; 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/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..6780108c4f 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); @@ -402,12 +400,12 @@ public: return mFlinger->setPowerModeInternal(display, mode); } - auto renderScreenImplLocked(const RenderArea& renderArea, + auto renderScreenImpl(const RenderArea& renderArea, SurfaceFlinger::TraverseLayersFunction traverseLayers, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem, bool regionSampling) { ScreenCaptureResults captureResults; - return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem, + return mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer, forSystem, regionSampling, false /* grayscale */, captureResults); } @@ -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/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 6ffc0396d7..0ef8456739 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -268,11 +268,8 @@ TEST_F(TimeStatsTest, canIncreaseGlobalStats) { for (size_t i = 0; i < MISSED_FRAMES; i++) { ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames()); } - TimeStats::ClientCompositionRecord record; - record.hadClientComposition = true; - for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) { - ASSERT_NO_FATAL_FAILURE(mTimeStats->pushCompositionStrategyState(record)); + ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames()); } SFTimeStatsGlobalProto globalProto; @@ -462,49 +459,19 @@ TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) { EXPECT_THAT(result, HasSubstr(expectedResult)); } -TEST_F(TimeStatsTest, canIncreaseClientCompositionStats) { +TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) { // this stat is not in the proto so verify by checking the string dump - constexpr size_t COMPOSITION_STRATEGY_CHANGED_FRAMES = 1; - constexpr size_t HAD_CLIENT_COMPOSITION_FRAMES = 2; - constexpr size_t REUSED_CLIENT_COMPOSITION_FRAMES = 3; - constexpr size_t COMPOSITION_STRATEGY_PREDICTION_SUCCEEDED_FRAMES = 4; - constexpr size_t COMPOSITION_STRATEGY_PREDICTED_FRAMES = 5; + constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2; EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); - for (size_t i = 0; i <= COMPOSITION_STRATEGY_PREDICTED_FRAMES; i++) { - TimeStats::ClientCompositionRecord record; - record.hadClientComposition = i < HAD_CLIENT_COMPOSITION_FRAMES; - record.changed = i < COMPOSITION_STRATEGY_CHANGED_FRAMES; - record.reused = i < REUSED_CLIENT_COMPOSITION_FRAMES; - record.predicted = i < COMPOSITION_STRATEGY_PREDICTED_FRAMES; - record.predictionSucceeded = i < COMPOSITION_STRATEGY_PREDICTION_SUCCEEDED_FRAMES; - mTimeStats->pushCompositionStrategyState(record); + for (size_t i = 0; i < CLIENT_COMPOSITION_REUSED_FRAMES; i++) { + ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames()); } const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); - std::string expected = - "compositionStrategyChanges = " + std::to_string(COMPOSITION_STRATEGY_CHANGED_FRAMES); - EXPECT_THAT(result, HasSubstr(expected)); - - expected = "clientCompositionFrames = " + std::to_string(HAD_CLIENT_COMPOSITION_FRAMES); - EXPECT_THAT(result, HasSubstr(expected)); - - expected = - "clientCompositionReusedFrames = " + std::to_string(REUSED_CLIENT_COMPOSITION_FRAMES); - EXPECT_THAT(result, HasSubstr(expected)); - - expected = "compositionStrategyPredicted = " + - std::to_string(COMPOSITION_STRATEGY_PREDICTED_FRAMES); - EXPECT_THAT(result, HasSubstr(expected)); - - expected = "compositionStrategyPredictionSucceeded = " + - std::to_string(COMPOSITION_STRATEGY_PREDICTION_SUCCEEDED_FRAMES); - EXPECT_THAT(result, HasSubstr(expected)); - - expected = "compositionStrategyPredictionFailed = " + - std::to_string(COMPOSITION_STRATEGY_PREDICTED_FRAMES - - COMPOSITION_STRATEGY_PREDICTION_SUCCEEDED_FRAMES); - EXPECT_THAT(result, HasSubstr(expected)); + const std::string expectedResult = + "clientCompositionReusedFrames = " + std::to_string(CLIENT_COMPOSITION_REUSED_FRAMES); + EXPECT_THAT(result, HasSubstr(expectedResult)); } TEST_F(TimeStatsTest, canIncreaseRefreshRateSwitches) { @@ -522,6 +489,21 @@ TEST_F(TimeStatsTest, canIncreaseRefreshRateSwitches) { EXPECT_THAT(result, HasSubstr(expectedResult)); } +TEST_F(TimeStatsTest, canIncreaseCompositionStrategyChanges) { + // this stat is not in the proto so verify by checking the string dump + constexpr size_t COMPOSITION_STRATEGY_CHANGES = 2; + + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + for (size_t i = 0; i < COMPOSITION_STRATEGY_CHANGES; i++) { + ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementCompositionStrategyChanges()); + } + + const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + const std::string expectedResult = + "compositionStrategyChanges = " + std::to_string(COMPOSITION_STRATEGY_CHANGES); + EXPECT_THAT(result, HasSubstr(expectedResult)); +} + TEST_F(TimeStatsTest, canAverageFrameDuration) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); mTimeStats->setPowerMode(PowerMode::ON); @@ -854,7 +836,7 @@ TEST_F(TimeStatsTest, canClearTimeStats) { ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames()); ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames()); - ASSERT_NO_FATAL_FAILURE(mTimeStats->pushCompositionStrategyState({})); + ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames()); ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(PowerMode::ON)); mTimeStats->recordFrameDuration(std::chrono::nanoseconds(3ms).count(), @@ -885,8 +867,9 @@ TEST_F(TimeStatsTest, canClearTimeStats) { TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { // These stats are not in the proto so verify by checking the string dump. EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); - ASSERT_NO_FATAL_FAILURE(mTimeStats->pushCompositionStrategyState({})); + ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames()); ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementRefreshRateSwitches()); + ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementCompositionStrategyChanges()); mTimeStats->setPowerMode(PowerMode::ON); mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(), std::chrono::nanoseconds(5ms).count()); @@ -1049,10 +1032,8 @@ TEST_F(TimeStatsTest, globalStatsCallback) { for (size_t i = 0; i < MISSED_FRAMES; i++) { mTimeStats->incrementMissedFrames(); } - TimeStats::ClientCompositionRecord record; - record.hadClientComposition = true; for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) { - mTimeStats->pushCompositionStrategyState(record); + mTimeStats->incrementClientCompositionFrames(); } insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); 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/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index a1aa7e8439..996f835148 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -76,6 +76,7 @@ public: MOCK_METHOD4(getDisplayRequests, Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*)); MOCK_METHOD2(getDozeSupport, Error(Display, bool*)); + MOCK_METHOD2(getKernelIdleTimerSupport, Error(Display, bool*)); MOCK_METHOD5(getHdrCapabilities, Error(Display, std::vector<Hdr>*, float*, float*, float*)); MOCK_METHOD1(getPerFrameMetadataKeys, std::vector<IComposerClient::PerFrameMetadataKey>(Display)); @@ -157,6 +158,8 @@ public: Error(Display, Layer, const std::vector<IComposerClient::Rect>&)); MOCK_METHOD2(getDisplayDecorationSupport, Error(Display, std::optional<DisplayDecorationSupport>*)); + MOCK_METHOD2(setIdleTimerEnabled, Error(Display, std::chrono::milliseconds)); + MOCK_METHOD2(hasDisplayIdleTimerCapability, Error(Display, bool*)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 570ffeb935..d9faa06ea3 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -99,6 +99,8 @@ public: hal::Error, getDisplayDecorationSupport, (std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport> *), (override)); + MOCK_METHOD(hal::Error, setIdleTimerEnabled, (std::chrono::milliseconds), (override)); + MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (), (const override)); }; class Layer : public HWC2::Layer { diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h index 0dee800558..0a69b562ab 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -33,7 +33,10 @@ public: MOCK_METHOD0(miniDump, std::string()); MOCK_METHOD0(incrementTotalFrames, void()); MOCK_METHOD0(incrementMissedFrames, void()); + MOCK_METHOD0(incrementClientCompositionFrames, void()); + MOCK_METHOD0(incrementClientCompositionReusedFrames, void()); MOCK_METHOD0(incrementRefreshRateSwitches, void()); + MOCK_METHOD0(incrementCompositionStrategyChanges, void()); MOCK_METHOD1(recordDisplayEventConnectionCount, void(int32_t)); MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t)); MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t)); @@ -60,8 +63,6 @@ public: void(hardware::graphics::composer::V2_4::IComposerClient::PowerMode)); MOCK_METHOD2(recordRefreshRate, void(uint32_t, nsecs_t)); MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&)); - MOCK_METHOD(void, pushCompositionStrategyState, - (const android::TimeStats::ClientCompositionRecord&), (override)); }; } // namespace android::mock diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 60c0cb5c3e..6e54d6c0de 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -632,10 +632,8 @@ 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; @@ -712,10 +710,8 @@ 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: @@ -924,23 +920,17 @@ VkResult EnumerateInstanceExtensionProperties( uint32_t* pPropertyCount, VkExtensionProperties* pProperties) { std::vector<VkExtensionProperties> loader_extensions; - 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}); + {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION}); loader_extensions.push_back({ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_SPEC_VERSION}); loader_extensions.push_back({ VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION}); - 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}); + loader_extensions.push_back( + {VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, + VK_KHR_GET_SURFACE_CAPABILITIES_2_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 b436db1de7..5f37a50a03 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -565,13 +565,11 @@ 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 079f9cca39..819f6b211c 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -41,13 +41,11 @@ 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 0be4403731..6dce394839 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -619,65 +619,43 @@ VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/, VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( - VkPhysicalDevice pdev, + VkPhysicalDevice /*pdev*/, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* capabilities) { ATRACE_CALL(); int err; - int width, height; - int transform_hint; - int max_buffer_count; - if (surface == VK_NULL_HANDLE) { - const InstanceData& instance_data = GetData(pdev); - ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; - bool surfaceless_enabled = - instance_data.hook_extensions.test(surfaceless); - if (!surfaceless_enabled) { - // 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(); + 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; - } + 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; + } - 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 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; + } - 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; - } + 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; } capabilities->minImageCount = std::min(max_buffer_count, 3); capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); @@ -718,43 +696,23 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, const InstanceData& instance_data = GetData(pdev); bool wide_color_support = false; - uint64_t consumer_usage = 0; - bool swapchain_ext = - instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); - 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; + 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; } - wide_color_support = wide_color_support && swapchain_ext; + ALOGV("wide_color_support is: %d", wide_color_support); + wide_color_support = + wide_color_support && + instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); AHardwareBuffer_Desc desc = {}; desc.width = 1; desc.height = 1; desc.layers = 1; - desc.usage = consumer_usage | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + desc.usage = surface.consumer_usage | + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; // We must support R8G8B8A8 @@ -762,11 +720,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; - if (swapchain_ext) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); - } - if (wide_color_support) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); @@ -774,10 +727,6 @@ 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{ @@ -865,12 +814,6 @@ 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; @@ -922,51 +865,31 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, int err; int query_value; - 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(); + ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &query_value); - if (err != android::OK || query_value < 0) { - ALOGE( - "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " - "value=%d", - strerror(-err), err, query_value); - return VK_ERROR_SURFACE_LOST_KHR; - } - uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); - - err = - window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); - if (err != android::OK || query_value < 0) { - ALOGE( - "NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d", - strerror(-err), err, query_value); - return VK_ERROR_SURFACE_LOST_KHR; - } - uint32_t max_buffer_count = static_cast<uint32_t>(query_value); + 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); - 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); + err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); + if (err != android::OK || query_value < 0) { + ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d", + strerror(-err), err, query_value); + return VK_ERROR_SURFACE_LOST_KHR; } + uint32_t max_buffer_count = static_cast<uint32_t>(query_value); + + std::vector<VkPresentModeKHR> present_modes; + if (min_undequeued_buffers + 1 < max_buffer_count) + 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 af56764a21..6a73023193 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -27,13 +27,11 @@ _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', ] |