diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-08 04:55:01 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-05-08 04:55:01 +0000 |
commit | 5cea12c8508aa438f67066490cb26101bb169b27 (patch) | |
tree | 10524a8c46844f072d95c56cadc67b5ecedfa42f | |
parent | f3f4033673fc34e371d02f02fd506c881a605994 (diff) | |
parent | 23fa3fb806fb8392708c3fa139dd4ef24cedfaaf (diff) | |
download | native-5cea12c8508aa438f67066490cb26101bb169b27.tar.gz |
Merge "Snap for 8554636 from 864034bf750090c0522ba6a3cd1abb379999ba6e to sdk-release" into sdk-releaseplatform-tools-33.0.2
299 files changed, 9649 insertions, 2912 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index 4b64203387..260ee8dd4f 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -67,5 +67,66 @@ } ] } + ], + "hwasan-postsubmit": [ + { + "name": "SurfaceFlinger_test", + "options": [ + { + "include-filter": "*CredentialsTest.*" + }, + { + "include-filter": "*SurfaceFlingerStress.*" + }, + { + "include-filter": "*SurfaceInterceptorTest.*" + }, + { + "include-filter": "*LayerTransactionTest.*" + }, + { + "include-filter": "*LayerTypeTransactionTest.*" + }, + { + "include-filter": "*LayerUpdateTest.*" + }, + { + "include-filter": "*GeometryLatchingTest.*" + }, + { + "include-filter": "*CropLatchingTest.*" + }, + { + "include-filter": "*ChildLayerTest.*" + }, + { + "include-filter": "*ScreenCaptureTest.*" + }, + { + "include-filter": "*ScreenCaptureChildOnlyTest.*" + }, + { + "include-filter": "*DereferenceSurfaceControlTest.*" + }, + { + "include-filter": "*BoundlessLayerTest.*" + }, + { + "include-filter": "*MultiDisplayLayerBoundsTest.*" + }, + { + "include-filter": "*InvalidHandleTest.*" + }, + { + "include-filter": "*VirtualDisplayTest.*" + }, + { + "include-filter": "*RelativeZTest.*" + }, + { + "include-filter": "*RefreshRateOverlayTest.*" + } + ] + } ] } diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 7aee795fc4..9bd733d04c 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -1224,10 +1224,7 @@ int main(int argc, char **argv) if (ret < 0) { for (int i = optind; i < argc; i++) { - if (!setCategoryEnable(argv[i])) { - fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]); - exit(1); - } + setCategoryEnable(argv[i]); } break; } @@ -1343,10 +1340,10 @@ int main(int argc, char **argv) // contain entries from only one CPU can cause "begin" entries without a // matching "end" entry to show up if a task gets migrated from one CPU to // another. - if (!onlyUserspace) + if (!onlyUserspace) { ok = clearTrace(); - - writeClockSyncMarker(); + writeClockSyncMarker(); + } if (ok && !async && !traceStream) { // Sleep to allow the trace to be captured. struct timespec timeLeft; diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 0e9ce897bc..890c15f895 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, @@ -2733,8 +2733,8 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& screenshot_fd_in, bool is_screenshot_requested) { // Duplicate the fds because the passed in fds don't outlive the binder transaction. - bugreport_fd.reset(dup(bugreport_fd_in.get())); - screenshot_fd.reset(dup(screenshot_fd_in.get())); + bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0)); + screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0)); SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested); } diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index c9f680b266..bf5e893a99 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -81,8 +81,9 @@ cc_defaults { "cert-*", "-cert-err58-cpp", ], - tidy_flags: [ - "-warnings-as-errors=clang-analyzer-security*,cert-*", + tidy_checks_as_errors: [ + "clang-analyzer-security*", + "cert-*", ], } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 4e3aae4312..95335e90ae 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -18,13 +18,9 @@ #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER -#include <algorithm> #include <errno.h> -#include <fstream> #include <fts.h> -#include <functional> #include <inttypes.h> -#include <regex> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -40,6 +36,12 @@ #include <sys/wait.h> #include <sys/xattr.h> #include <unistd.h> +#include <algorithm> +#include <filesystem> +#include <fstream> +#include <functional> +#include <regex> +#include <unordered_set> #include <android-base/file.h> #include <android-base/logging.h> @@ -53,7 +55,8 @@ #include <cutils/fs.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> -#include <log/log.h> // TODO: Move everything to base/logging. +#include <linux/quota.h> +#include <log/log.h> // TODO: Move everything to base/logging. #include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> #include <private/android_projectid_config.h> @@ -80,6 +83,7 @@ // #define GRANULAR_LOCKS using android::base::ParseUint; +using android::base::Split; using android::base::StringPrintf; using std::endl; @@ -114,6 +118,12 @@ static constexpr const char* kMntFuse = "/mnt/pass_through/0/"; static std::atomic<bool> sAppDataIsolationEnabled(false); +/** + * Flag to control if project ids are supported for internal storage + */ +static std::atomic<bool> sUsingProjectIdsFlag(false); +static std::once_flag flag; + namespace { constexpr const char* kDump = "android.permission.DUMP"; @@ -455,14 +465,41 @@ 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 bool internal_storage_has_project_id() { + // The following path is populated in setFirstBoot, so if this file is present + // then project ids can be used. Using call once to cache the result of this check + // to avoid having to check the file presence again and again. + std::call_once(flag, []() { + auto using_project_ids = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + sUsingProjectIdsFlag = access(using_project_ids.c_str(), F_OK) == 0; + }); + // return sUsingProjectIdsFlag; + return false; +} + +static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid, + long project_id) { + if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { PLOG(ERROR) << "Failed to prepare " << path; return -1; } + if (internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } return 0; } +static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, + uid_t uid, gid_t gid, long project_id) { + auto path = StringPrintf("%s/%s", parent.c_str(), name); + int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + if (ret == 0 && internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } + return ret; +} + static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { return true; @@ -596,9 +633,10 @@ 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, + long projectIdApp, long projectIdCache) { struct stat st{}; bool parent_dir_exists = (stat(path.c_str(), &st) == 0); @@ -608,23 +646,17 @@ static binder::Status createAppDataDirs(const std::string& path, bool code_cache_exists = (access(code_cache_path.c_str(), F_OK) == 0); if (parent_dir_exists) { - if (*previousUid < 0) { - // If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner - // of the directory as previousUid. This is required because it is not always possible - // to chown app data during app upgrade (e.g. secondary users' CE storage not unlocked) - *previousUid = st.st_uid; - } - if (*previousUid != uid) { - if (!chown_app_dir(path, uid, *previousUid, cacheGid)) { + if (previousUid > 0 && previousUid != uid) { + if (!chown_app_dir(path, uid, previousUid, cacheGid)) { return error("Failed to chown " + 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, projectIdApp) || + prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid, projectIdCache) || + prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid, projectIdCache)) { return error("Failed to prepare " + path); } @@ -665,12 +697,9 @@ binder::Status InstalldNativeService::createAppDataLocked( int32_t uid = multiuser_get_uid(userId, appId); - // If previousAppId < 0, we will use the existing app data owner as previousAppUid - // If previousAppId == 0, we use uid as previousUid (no data migration will happen) - // if previousAppId > 0, an app is upgrading and changing its app ID - int32_t previousUid = previousAppId > 0 - ? (int32_t) multiuser_get_uid(userId, previousAppId) - : (previousAppId == 0 ? uid : -1); + // If previousAppId > 0, an app is changing its app ID + int32_t previousUid = + previousAppId > 0 ? (int32_t)multiuser_get_uid(userId, previousAppId) : -1; int32_t cacheGid = multiuser_get_cache_gid(userId, appId); mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; @@ -680,10 +709,14 @@ binder::Status InstalldNativeService::createAppDataLocked( cacheGid = uid; } + long projectIdApp = get_project_id(uid, PROJECT_ID_APP_START); + long projectIdCache = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + 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, + projectIdApp, projectIdCache); if (!status.isOk()) { return status; } @@ -708,11 +741,12 @@ 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, + projectIdApp, projectIdCache); if (!status.isOk()) { return status; } - if (previousUid != uid) { + if (previousUid > 0 && previousUid != uid) { chown_app_profile_dir(packageName, appId, userId); } @@ -720,6 +754,59 @@ binder::Status InstalldNativeService::createAppDataLocked( return error("Failed to prepare profiles for " + packageName); } } + + if (flags & FLAG_STORAGE_SDK) { + // Safe to ignore status since we can retry creating this by calling reconcileSdkData + auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, 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/<package-name> directory and other + * app level sub directories, such as ./shared + */ +binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + 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(); + } + + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + if ((flags & currentFlag) == 0) { + continue; + } + bool isCeData = (currentFlag == FLAG_STORAGE_CE); + + // /data/misc_{ce,de}/<user-id>/sdksandbox directory gets created by vold + // during user creation + + // 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 + + if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM, 0)) { + return error("Failed to prepare " + packagePath); + } + } + return ok(); } @@ -766,6 +853,111 @@ 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); + int32_t appUid = multiuser_get_uid(userId, appId); + long projectIdApp = get_project_id(appUid, PROJECT_ID_APP_START); + long projectIdCache = get_project_id(appUid, PROJECT_ID_APP_CACHE_START); + auto status = + createAppDataDirs(path, sandboxUid, AID_NOBODY, previousSandboxUid, cacheGid, + seInfo, 0700 | S_ISGID, projectIdApp, projectIdCache); + 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); @@ -911,6 +1103,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; } @@ -1007,6 +1240,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; } @@ -1492,6 +1751,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. @@ -1519,6 +1808,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; } @@ -1550,8 +1851,14 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s binder::Status res = ok(); if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the user_de dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + 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); @@ -1566,12 +1873,19 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s } if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the user_ce dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + 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); + // Contents only, as vold is responsible for the media dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); } } return res; @@ -1808,69 +2122,78 @@ static std::string toString(std::vector<int64_t> values) { return res.str(); } #endif +// On devices without sdcardfs, if internal and external are on +// the same volume, a uid such as u0_a123 is used for both +// internal and external storage; therefore, subtract that +// amount from internal to make sure we don't count it double. +// This needs to happen for data, cache and OBB +static void deductDoubleSpaceIfNeeded(stats* stats, int64_t doubleSpaceToBeDeleted, uid_t uid, + const std::string& uuid) { + if (!supports_sdcardfs()) { + stats->dataSize -= doubleSpaceToBeDeleted; + long obbProjectId = get_project_id(uid, PROJECT_ID_EXT_OBB_START); + int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); + stats->dataSize -= appObbSize; + } +} static void collectQuotaStats(const std::string& uuid, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { - int64_t space; + int64_t space, doubleSpaceToBeDeleted = 0; uid_t uid = multiuser_get_uid(userId, appId); - if (stats != nullptr) { - if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { - stats->dataSize += space; - } - - int cacheGid = multiuser_get_cache_gid(userId, appId); - if (cacheGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { - stats->cacheSize += space; - } - } - - int sharedGid = multiuser_get_shared_gid(0, appId); - if (sharedGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { - stats->codeSize += space; - } - } - } + static const bool supportsProjectId = internal_storage_has_project_id(); if (extStats != nullptr) { - static const bool supportsSdCardFs = supports_sdcardfs(); space = get_occupied_app_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for - // application dirs on both internal and external storage; - // therefore, substract that amount from internal to make sure - // we don't count it double. - stats->dataSize -= space; - } + doubleSpaceToBeDeleted += space; } space = get_occupied_app_cache_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; // cache counts for "data" extStats->cacheSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for both - // internal and external storage; therefore, substract that - // amount from internal to make sure we don't count it double. - stats->dataSize -= space; + doubleSpaceToBeDeleted += space; + } + } + + if (stats != nullptr) { + if (!supportsProjectId) { + if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { + stats->dataSize += space; + } + deductDoubleSpaceIfNeeded(stats, doubleSpaceToBeDeleted, uid, uuid); + 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) { + stats->cacheSize += space; + } + } + } else { + long projectId = get_project_id(uid, PROJECT_ID_APP_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->dataSize += space; + } + projectId = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->cacheSize += space; + stats->dataSize += space; } } - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, the UID of OBBs on external storage - // matches the regular app UID (eg u0_a123); therefore, to avoid - // OBBs being include in stats->dataSize, compute the OBB size for - // this app, and substract it from the size reported on internal - // storage - long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START; - int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); - stats->dataSize -= appObbSize; + int sharedGid = multiuser_get_shared_gid(0, appId); + if (sharedGid != -1) { + if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { + stats->codeSize += space; + } } } } @@ -1925,8 +2248,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; @@ -1951,6 +2283,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); } @@ -1993,6 +2330,11 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st fts_close(fts); } static bool ownsExternalStorage(int32_t appId) { + // if project id calculation is supported then, there is no need to + // calculate in a different way and project_id based calculation can work + if (internal_storage_has_project_id()) { + return false; + } // Fetch external storage owner appid and check if it is the same as the // current appId whose size is calculated struct stat s; @@ -2093,6 +2435,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( @@ -2329,6 +2684,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); @@ -2850,6 +3212,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& oatDir, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); @@ -2984,6 +3389,33 @@ binder::Status InstalldNativeService::hashSecondaryDexFile( return result ? ok() : error(); } +bool check_if_ioctl_feature_is_supported() { + bool result = false; + auto temp_path = StringPrintf("%smisc/installd/ioctl_check", android_data_dir.c_str()); + if (access(temp_path.c_str(), F_OK) != 0) { + open(temp_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); + result = set_quota_project_id(temp_path, 0, false) == 0; + // delete the temp file + // remove the external file + remove(temp_path.c_str()); + } + return result; +} + +binder::Status InstalldNativeService::setFirstBoot() { + ENFORCE_UID(AID_SYSTEM); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + std::string uuid; + if (GetOccupiedSpaceForProjectId(uuid, 0) != -1 && check_if_ioctl_feature_is_supported()) { + auto first_boot_path = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + if (access(first_boot_path.c_str(), F_OK) != 0) { + open(first_boot_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); + } + } + return ok(); +} + binder::Status InstalldNativeService::invalidateMounts() { ENFORCE_UID(AID_SYSTEM); std::lock_guard<std::recursive_mutex> lock(mMountsLock); @@ -3187,11 +3619,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 96783c3dbb..95ac51692d 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -47,14 +47,9 @@ public: int32_t flags); binder::Status createAppData(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, - int64_t* _aidl_return); - binder::Status createAppDataLocked(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, - int32_t flags, int32_t appId, int32_t previousAppId, - const std::string& seInfo, int32_t targetSdkVersion, - int64_t* _aidl_return); + const std::string& packageName, int32_t userId, int32_t flags, + int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return); binder::Status createAppData( const android::os::CreateAppDataArgs& args, @@ -63,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, @@ -172,6 +167,7 @@ public: int32_t storageFlag, std::vector<uint8_t>* _aidl_return); binder::Status invalidateMounts(); + binder::Status setFirstBoot(); binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid, bool* _aidl_return); binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid); @@ -202,6 +198,34 @@ private: std::unordered_map<uid_t, int64_t> mCacheQuotas; std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid); + + binder::Status createAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + 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 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..fc4cfc98dc 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -30,6 +30,14 @@ }, { "name": "CtsCompilationTestCases" + }, + { + "name": "SdkSandboxStorageHostTest", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + } + ] } ] } diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index f4fd9a94de..c17c6bfce3 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -20,10 +20,12 @@ package android.os; interface IInstalld { void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags); void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags); - + void setFirstBoot(); android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args); android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args); + void reconcileSdkData(in android.os.ReconcileSdkDataArgs args); + void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, int appId, @utf8InCpp String seInfo); void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @@ -131,6 +133,7 @@ interface IInstalld { const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; + const int FLAG_STORAGE_SDK = 0x8; const int FLAG_CLEAR_CACHE_ONLY = 0x10; const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl new file mode 100644 index 0000000000..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 9702d42b14..effb4013f1 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) { @@ -74,8 +78,20 @@ std::string get_package_name(uid_t uid) { } namespace installd { -constexpr const char* kTestUuid = "TEST"; -constexpr const char* kTestPath = "/data/local/tmp/user/0"; +static constexpr const char* kTestUuid = "TEST"; +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 @@ -97,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); @@ -116,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) { @@ -132,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; } @@ -155,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); }); } @@ -173,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)); @@ -383,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)); @@ -393,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)); @@ -408,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)); } @@ -441,7 +465,7 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } -TEST_F(ServiceTest, GetAppSize) { +TEST_F(ServiceTest, GetAppSizeManualForMedia) { struct stat s; std::string externalPicDir = @@ -485,6 +509,100 @@ TEST_F(ServiceTest, GetAppSize) { system(removeCommand.c_str()); } } +// TEST_F(ServiceTest, GetAppSizeProjectID_UID) { +// struct stat s; +// std::string externalPicDir = +// StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str()); +// if (stat(externalPicDir.c_str(), &s) == 0) { +// // fetch the appId from the uid of the external storage owning app +// int32_t externalStorageAppId = multiuser_get_app_id(s.st_uid); +// // Fetch Package Name for the external storage owning app uid +// std::string pkg = get_package_name(s.st_uid); +// +// std::vector<int64_t> externalStorageSize, externalStorageSizeAfterAddingCacheFile; +// std::vector<int64_t> ceDataInodes; +// +// std::vector<std::string> codePaths; +// std::vector<std::string> packageNames; +// // set up parameters +// packageNames.push_back(pkg); +// ceDataInodes.push_back(0); +// // initialise the mounts +// service->invalidateMounts(); +// auto using_project_ids = +// StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); +// bool usingProjectIds = access(using_project_ids.c_str(), F_OK) == 0; +// if (!usingProjectIds) { +// service->setFirstBoot(); +// } +// +// if (access(using_project_ids.c_str(), F_OK) != 0) { +// // projectids is not used, so check that ioctl features should be absent +// auto temp_path = StringPrintf("%smisc/installd/ioctl_check", +// android_data_dir.c_str()); +// +// if (access(temp_path.c_str(), F_OK) != 0) { +// open(temp_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); +// bool result = set_quota_project_id(temp_path, 0, false) == 0; +// // delete the temp file +// // remove the external file +// remove(temp_path.c_str()); +// // since using_project_ids file is not present, so ioctl settings should be +// absent +// // that is denoted by the result of setting project id flag as false +// ASSERT_FALSE(result); +// } +// } +// // call the getAppSize to get the current size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize); +// // add a file with 20MB size to the external storage +// std::string externalStorageCacheDir = +// StringPrintf("%s/%s/cache", create_data_user_ce_path(nullptr, 0).c_str(), +// pkg.c_str()); +// std::string cacheFileLocation = +// StringPrintf("%s/%s", externalStorageCacheDir.c_str(), "External.jpg"); +// std::string externalFileContentCommand = +// StringPrintf("dd if=/dev/zero of=%s bs=1M count=20", cacheFileLocation.c_str()); +// system(externalFileContentCommand.c_str()); +// // call the getAppSize again to get the new size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, +// &externalStorageSizeAfterAddingCacheFile); +// // check that the size of cache and data increases when cache file is added +// int64_t sizeDiffData = externalStorageSizeAfterAddingCacheFile[1] - +// externalStorageSize[1]; int64_t sizeDiffCache = +// externalStorageSizeAfterAddingCacheFile[2] - externalStorageSize[2]; +// ASSERT_TRUE(sizeDiffData == sizeDiffCache); +// // remove the external file +// std::string removeCommand = StringPrintf("rm -f %s", cacheFileLocation.c_str()); +// system(removeCommand.c_str()); +// // remove the setFirstBoot setting +// std::string removeCommand2 = "rm -f /data/misc/installd/using_project_ids"; +// system(removeCommand2.c_str()); +// // Do now without project id +// std::vector<int64_t> sizeWithUID, sizeWithUIDAfterAddingCacheFile; +// // call the getAppSize to get the current size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, &sizeWithUID); +// // add a file with 20MB size to the external storage +// system(externalFileContentCommand.c_str()); +// // call the getAppSize again to get the new size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, +// &sizeWithUIDAfterAddingCacheFile); +// // check that the size of cache and data increases when cache file is added +// sizeDiffData = sizeWithUIDAfterAddingCacheFile[1] - sizeWithUID[1]; +// sizeDiffCache = sizeWithUIDAfterAddingCacheFile[2] - sizeWithUID[2]; +// ASSERT_TRUE(sizeDiffData == sizeDiffCache); +// // remove the external file +// system(removeCommand.c_str()); +// // reset the using_project_id if it was initially set +// if (usingProjectIds) { +// service->setFirstBoot(); +// } +// } +// } TEST_F(ServiceTest, GetAppSizeWrongSizes) { int32_t externalStorageAppId = -1; std::vector<int64_t> externalStorageSize; @@ -943,5 +1061,364 @@ TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE)); } +class SdkSandboxDataTest : public testing::Test { +public: + 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(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(const std::string& packageName) { + android::os::CreateAppDataArgs args; + args.uuid = kTestUuid; + args.packageName = packageName; + args.userId = kTestUserId; + args.appId = kTestAppId; + args.seInfo = "default"; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK; + return args; + } + + android::os::ReconcileSdkDataArgs reconcileSdkDataArgs( + 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; + + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); + + service = new InstalldNativeService(); + clearAppData(); + ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700)); + ASSERT_TRUE(mkdirs("/data/local/tmp/user_de/0", 0700)); + ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/sdksandbox", 0700)); + ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/sdksandbox", 0700)); + + init_globals_from_data_and_root(); + } + + virtual void TearDown() { + delete service; + clearAppData(); + } + +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)); + } +}; + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo"; + CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751); + + const std::string fooDePath = "misc_de/0/sdksandbox/com.foo"; + CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + 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_WithoutSdkFlagDeletesExisting) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); + + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_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, 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_CreatesSdkPackageData_WithoutCeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK; + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + // CE paths should not exist + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + + // Only DE paths should exist + 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 } // namespace android diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 514b88135a..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,59 @@ 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", + create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/true, 0)); + EXPECT_EQ("/data/misc_ce/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, true, 10)); + + EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, true, 0, "com.foo")); + EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, true, 10, "com.foo")); + + EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo/shared", + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 0, "com.foo", "shared")); + EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared", + 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", + create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/false, 0)); + EXPECT_EQ("/data/misc_de/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, false, 10)); + + EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, false, 0, "com.foo")); + EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, false, 10, "com.foo")); + + EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo/shared", + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 0, "com.foo", "shared")); + EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared", + 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) { pid_t pid = fork(); if (pid == 0) { diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index f04ee337d9..c7bea3f4a9 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -37,6 +37,7 @@ #include <android-base/unique_fd.h> #include <cutils/fs.h> #include <cutils/properties.h> +#include <linux/fs.h> #include <log/log.h> #include <private/android_filesystem_config.h> #include <private/android_projectid_config.h> @@ -197,6 +198,45 @@ std::string create_data_user_de_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/user_de/%u", data.c_str(), userid); } +/** + * Create the path name where sdk_sandbox data for all apps will be stored. + * E.g. /data/misc_ce/0/sdksandbox + */ +std::string create_data_misc_sdk_sandbox_path(const char* uuid, bool isCeData, userid_t user) { + std::string data(create_data_path(uuid)); + if (isCeData) { + return StringPrintf("%s/misc_ce/%d/sdksandbox", data.c_str(), user); + } else { + return StringPrintf("%s/misc_de/%d/sdksandbox", data.c_str(), user); + } +} + +/** + * Create the path name where code data for all codes in a particular app will be stored. + * E.g. /data/misc_ce/0/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) { + check_package_name(package_name); + return StringPrintf("%s/%s", + create_data_misc_sdk_sandbox_path(volume_uuid, isCeData, user).c_str(), + package_name); +} + +/** + * 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_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(), + sub_dir_name); +} + std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) { return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user); } @@ -384,7 +424,44 @@ std::vector<userid_t> get_known_users(const char* volume_uuid) { return users; } +long get_project_id(uid_t uid, long start_project_id_range) { + return uid - AID_APP_START + start_project_id_range; +} + +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit) { + struct fsxattr fsx; + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << path << " to set project id."; + return -1; + } + + if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id."; + return -1; + } + + fsx.fsx_projid = project_id; + if (ioctl(fd, FS_IOC_FSSETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to set project id on " << path; + return -1; + } + if (set_inherit) { + unsigned int flags; + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance."; + return -1; + } + + flags |= FS_PROJINHERIT_FL; + if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance."; + return -1; + } + } + return 0; +} int calculate_tree_size(const std::string& path, int64_t* size, int32_t include_gid, int32_t exclude_gid, bool exclude_apps) { FTS *fts; @@ -628,16 +705,16 @@ static int rename_delete_dir_contents(const std::string& pathname, auto temp_dir_path = base::StringPrintf("%s/%s", Dirname(pathname).c_str(), temp_dir_name.c_str()); - if (::rename(pathname.c_str(), temp_dir_path.c_str())) { + auto dir_to_delete = temp_dir_path.c_str(); + if (::rename(pathname.c_str(), dir_to_delete)) { if (ignore_if_missing && (errno == ENOENT)) { return 0; } - ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), temp_dir_path.c_str(), - strerror(errno)); - return -errno; + ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), dir_to_delete, strerror(errno)); + dir_to_delete = pathname.c_str(); } - return delete_dir_contents(temp_dir_path.c_str(), 1, exclusion_predicate, ignore_if_missing); + return delete_dir_contents(dir_to_delete, 1, exclusion_predicate, ignore_if_missing); } bool is_renamed_deleted_dir(const std::string& path) { @@ -659,6 +736,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 39738945e4..ecea1d2b1c 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -32,6 +32,7 @@ #define MEASURE_DEBUG 0 #define FIXUP_DEBUG 0 +#define SDK_DEBUG 1 #define BYPASS_QUOTA 0 #define BYPASS_SDCARDFS 0 @@ -60,6 +61,14 @@ std::string create_data_user_de_package_path(const char* volume_uuid, std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name); +std::string create_data_misc_sdk_sandbox_path(const char* volume_uuid, bool isCeData, + userid_t userid); +std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData, + userid_t userid, const char* package_name); +std::string create_data_misc_sdk_sandbox_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); std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, @@ -123,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, @@ -160,6 +171,8 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta uid_t uid, gid_t gid); bool supports_sdcardfs(); +long get_project_id(uid_t uid, long start_project_id_range); +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit); int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId); int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId); diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 555be1ed7e..3cfe5297ca 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -295,28 +295,27 @@ bool isValidServiceName(const std::string& name) { Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) { auto ctx = mAccess->getCallingContext(); - // apps cannot add services if (multiuser_get_app_id(ctx.uid) >= AID_APP) { - return Status::fromExceptionCode(Status::EX_SECURITY); + return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services"); } if (!mAccess->canAdd(ctx, name)) { - return Status::fromExceptionCode(Status::EX_SECURITY); + return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial"); } if (binder == nullptr) { - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder"); } if (!isValidServiceName(name)) { LOG(ERROR) << "Invalid service name: " << name; - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name"); } #ifndef VENDORSERVICEMANAGER if (!meetsDeclarationRequirements(binder, name)) { // already logged - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error"); } #endif // !VENDORSERVICEMANAGER @@ -324,7 +323,7 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi if (binder->remoteBinder() != nullptr && binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) { LOG(ERROR) << "Could not linkToDeath when adding " << name; - return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure"); } // Overwrite the old service if it exists diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index 03a2709075..a177027e5c 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto @@ -101,6 +101,10 @@ message LayerStackChange { required uint32 layer_stack = 1; } +message DisplayFlagsChange { + required uint32 flags = 1; +} + message HiddenFlagChange { required bool hidden_flag = 1; } @@ -121,6 +125,7 @@ message DisplayChange { LayerStackChange layer_stack = 3; SizeChange size = 4; ProjectionChange projection = 5; + DisplayFlagsChange flags = 6; } } @@ -217,4 +222,4 @@ message BlurRegionChange { message Origin { required int32 pid = 1; required int32 uid = 2; -}
\ No newline at end of file +} diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 31dee235e8..34d53a52a0 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -221,6 +221,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.software.opengles.deqp.level-2022-03-01.prebuilt.xml", + src: "android.software.opengles.deqp.level-2022-03-01.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.software.sip.voip.prebuilt.xml", src: "android.software.sip.voip.xml", defaults: ["frameworks_native_data_etc_defaults"], @@ -239,6 +245,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.software.vulkan.deqp.level-2022-03-01.prebuilt.xml", + src: "android.software.vulkan.deqp.level-2022-03-01.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "aosp_excluded_hardware.prebuilt.xml", src: "aosp_excluded_hardware.xml", defaults: ["frameworks_native_data_etc_defaults"], @@ -255,3 +267,11 @@ prebuilt_etc { src: "handheld_core_hardware.xml", defaults: ["frameworks_native_data_etc_defaults"], } + +prebuilt_etc { + name: "android.software.app_compat_overrides.xml", + product_specific: true, + sub_dir: "permissions", + src: "android.software.app_compat_overrides.xml", + filename_from_src: true, +} diff --git a/data/etc/android.software.app_compat_overrides.xml b/data/etc/android.software.app_compat_overrides.xml new file mode 100644 index 0000000000..2f9726a3f0 --- /dev/null +++ b/data/etc/android.software.app_compat_overrides.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices that are opted-in to receive per-app compatibility + overrides. --> +<permissions> + <feature name="android.software.app_compat_overrides" /> +</permissions> diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h index 4c83a14728..ee392fc401 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h @@ -235,7 +235,7 @@ void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29); * * Available since API level 33. */ -int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN(33); +int android_tag_socket_with_uid(int sockfd, uint32_t tag, uid_t uid) __INTRODUCED_IN(33); /* * Set the socket tag for traffic statistics on the specified socket. @@ -245,14 +245,26 @@ int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN( * opened by another UID or was previously tagged by another UID. Subsequent * calls always replace any existing parameters. The socket tag is kept when the * socket is sent to another process using binder IPCs or other mechanisms such - * as UNIX socket fd passing. + * as UNIX socket fd passing. The tag is a value defined by the caller and used + * together with uid for data traffic accounting, so that the function callers + * can account different types of data usage for a uid. * * Returns 0 on success, or a negative POSIX error code (see errno.h) on * failure. * + * Some possible error codes: + * -EBADF Bad socketfd. + * -EPERM No permission. + * -EAFNOSUPPORT Socket family is neither AF_INET nor AF_INET6. + * -EPROTONOSUPPORT Socket protocol is neither IPPROTO_UDP nor IPPROTO_TCP. + * -EMFILE Too many stats entries. + * There are still other error codes that may provided by -errno of + * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by + * BPF maps read/write sys calls, which are set appropriately. + * * Available since API level 33. */ -int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33); +int android_tag_socket(int sockfd, uint32_t tag) __INTRODUCED_IN(33); /* * Untag a network socket. @@ -267,6 +279,12 @@ int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33); * Returns 0 on success, or a negative POSIX error code (see errno.h) on * failure. * + * One of possible error code: + * -EBADF Bad socketfd. + * Other error codes are either provided by -errno of + * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by + * BPF map element deletion sys call, which are set appropriately. + * * Available since API level 33. */ int android_untag_socket(int sockfd) __INTRODUCED_IN(33); 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/include/input/Flags.h b/include/ftl/Flags.h index b12a9ed2c5..27c84769cb 100644 --- a/include/input/Flags.h +++ b/include/ftl/Flags.h @@ -22,11 +22,10 @@ #include <string> #include <type_traits> -#include "NamedEnum.h" +#include <ftl/NamedEnum.h> #include "utils/BitSet.h" -#ifndef __UI_INPUT_FLAGS_H -#define __UI_INPUT_FLAGS_H +#pragma once namespace android { @@ -279,5 +278,3 @@ Flags<F> operator|(F lhs, F rhs) { } // namespace flag_operators } // namespace android - -#endif // __UI_INPUT_FLAGS_H diff --git a/include/input/NamedEnum.h b/include/ftl/NamedEnum.h index 8d3f6b7946..6e98feeb87 100644 --- a/include/input/NamedEnum.h +++ b/include/ftl/NamedEnum.h @@ -21,8 +21,7 @@ #include <optional> #include <string> -#ifndef __UI_INPUT_NAMEDENUM_H -#define __UI_INPUT_NAMEDENUM_H +#pragma once namespace android { @@ -128,5 +127,3 @@ public: }; } // namespace android - -#endif // __UI_INPUT_NAMEDENUM_H
\ No newline at end of file diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 5e40ca7ece..a6213f3ddd 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -18,8 +18,9 @@ #define _LIBINPUT_DISPLAY_VIEWPORT_H #include <android-base/stringprintf.h> +#include <ftl/NamedEnum.h> +#include <gui/constants.h> #include <input/Input.h> -#include <input/NamedEnum.h> #include <cinttypes> #include <optional> diff --git a/include/input/Input.h b/include/input/Input.h index dce6ccb3d5..4adaa5b1c5 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -124,15 +124,6 @@ constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS = AMOTION_EVENT_FLAG_WINDOW_IS_OBS constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20; enum { - /* Used when a motion event is not associated with any display. - * Typically used for non-pointer events. */ - ADISPLAY_ID_NONE = -1, - - /* The default display id. */ - ADISPLAY_ID_DEFAULT = 0, -}; - -enum { /* * Indicates that an input device has switches. * This input source flag is hidden from the API because switches are only used by the system @@ -354,12 +345,6 @@ private: */ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN(); -/** - * Invalid value for display size. Used when display size isn't available for an event or doesn't - * matter. This is just a constant 0 so that it has no effect if unused. - */ -constexpr int32_t AMOTION_EVENT_INVALID_DISPLAY_SIZE = 0; - /* * Pointer coordinate data. */ @@ -592,6 +577,8 @@ public: void setCursorPosition(float x, float y); + uint32_t getDisplayOrientation() const { return mDisplayOrientation; } + int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } @@ -768,8 +755,8 @@ public: int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float rawXCursorPosition, - float rawYCursorPosition, int32_t displayWidth, int32_t displayHeight, - nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + float rawYCursorPosition, uint32_t displayOrientation, int32_t displayWidth, + int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -827,6 +814,7 @@ protected: float mYPrecision; float mRawXCursorPosition; float mRawYCursorPosition; + uint32_t mDisplayOrientation; int32_t mDisplayWidth; int32_t mDisplayHeight; nsecs_t mDownTime; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 360dfbfd73..a790b5637f 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -136,10 +136,10 @@ struct InputMessage { float yPrecision; float xCursorPosition; float yCursorPosition; + uint32_t displayOrientation; int32_t displayWidth; int32_t displayHeight; uint32_t pointerCount; - uint32_t empty3; /** * The "pointers" field must be the last field of the struct InputMessage. * When we send the struct InputMessage across the socket, we are not @@ -355,8 +355,9 @@ public: int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, int32_t displayWidth, int32_t displayHeight, - nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + float yCursorPosition, uint32_t displayOrientation, + int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, + nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 451ca3c6cd..f6f8939b7a 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -84,7 +84,7 @@ public: const std::string getLoadFileName() const; - /* Combines this key character map with an overlay. */ + /* Combines this key character map with the provided overlay. */ void combine(const KeyCharacterMap& overlay); /* Gets the keyboard type. */ @@ -144,6 +144,8 @@ public: bool operator==(const KeyCharacterMap& other) const; + bool operator!=(const KeyCharacterMap& other) const; + KeyCharacterMap(const KeyCharacterMap& other); virtual ~KeyCharacterMap(); @@ -230,11 +232,12 @@ private: KeyedVector<int32_t, Key*> mKeys; KeyboardType mType; std::string mLoadFileName; + bool mLayoutOverlayApplied; KeyedVector<int32_t, int32_t> mKeysByScanCode; KeyedVector<int32_t, int32_t> mKeysByUsageCode; - KeyCharacterMap(); + KeyCharacterMap(const std::string& filename); bool getKey(int32_t keyCode, const Key** outKey) const; bool getKeyBehavior(int32_t keyCode, int32_t metaState, @@ -243,8 +246,6 @@ private: bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; - static base::Result<std::shared_ptr<KeyCharacterMap>> load(Tokenizer* tokenizer, Format format); - static void addKey(Vector<KeyEvent>& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time); static void addMetaKeys(Vector<KeyEvent>& outEvents, @@ -264,6 +265,15 @@ private: int32_t deviceId, int32_t metaState, nsecs_t time, int32_t keyCode, int32_t keyMetaState, int32_t* currentMetaState); + + /* Clears all data stored in this key character map */ + void clear(); + + /* Loads the KeyCharacterMap provided by the tokenizer into this instance. */ + status_t load(Tokenizer* tokenizer, Format format); + + /* Reloads the data from mLoadFileName and unapplies any overlay. */ + status_t reloadBaseFromFile(); }; } // namespace android 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..39befbe8e0 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: { @@ -579,6 +582,10 @@ void BBinder::removeRpcServerLink(const sp<RpcServerLink>& link) { BBinder::~BBinder() { + if (!wasParceled() && getExtension()) { + ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + } + Extras* e = mExtras.load(std::memory_order_relaxed); if (e) delete e; } @@ -590,6 +597,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/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h index fdd532aec6..57e03cb3a6 100644 --- a/libs/binder/BufferedTextOutput.h +++ b/libs/binder/BufferedTextOutput.h @@ -18,8 +18,8 @@ #define ANDROID_BUFFEREDTEXTOUTPUT_H #include <binder/TextOutput.h> -#include <utils/threads.h> #include <sys/uio.h> +#include <utils/Mutex.h> // --------------------------------------------------------------------------- namespace android { diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index bd974b02b1..c6b0cb7b01 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -31,8 +31,9 @@ #include <binder/Parcel.h> #include <log/log.h> -#include <utils/KeyedVector.h> -#include <utils/threads.h> +#include <utils/Mutex.h> + +#include <map> #define VERBOSE 0 @@ -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..3c97dcab93 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -27,7 +27,6 @@ #include <utils/CallStack.h> #include <utils/Log.h> #include <utils/SystemClock.h> -#include <utils/threads.h> #include <atomic> #include <errno.h> @@ -1199,7 +1198,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 +1409,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/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index c4475c7780..03553f36e9 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -23,7 +23,6 @@ #include <utils/Log.h> #include <utils/SortedVector.h> #include <utils/String8.h> -#include <utils/threads.h> #include <stdint.h> #include <stdio.h> 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..58b0b35323 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)) { @@ -569,6 +569,47 @@ bool Parcel::hasFileDescriptors() const return mHasFds; } +std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { + std::vector<sp<IBinder>> ret; + + size_t initPosition = dataPosition(); + for (size_t i = 0; i < mObjectsSize; i++) { + binder_size_t offset = mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_BINDER) continue; + + setDataPosition(offset); + + sp<IBinder> binder = readStrongBinder(); + if (binder != nullptr) ret.push_back(binder); + } + + setDataPosition(initPosition); + return ret; +} + +std::vector<int> Parcel::debugReadAllFileDescriptors() const { + std::vector<int> ret; + + size_t initPosition = dataPosition(); + for (size_t i = 0; i < mObjectsSize; i++) { + binder_size_t offset = mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_FD) continue; + + setDataPosition(offset); + + int fd = readFileDescriptor(); + LOG_ALWAYS_FATAL_IF(fd == -1); + ret.push_back(fd); + } + + setDataPosition(initPosition); + return ret; +} + status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const { if (len > INT32_MAX || offset > INT32_MAX) { // Don't accept size_t values which may have come from an inadvertent conversion from a @@ -1543,6 +1584,7 @@ status_t Parcel::readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const template<class T> status_t Parcel::readAligned(T *pArg) const { static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); + static_assert(std::is_trivially_copyable_v<T>); if ((mDataPos+sizeof(T)) <= mDataSize) { if (mObjectsSize > 0) { @@ -1554,9 +1596,8 @@ status_t Parcel::readAligned(T *pArg) const { } } - const void* data = mData+mDataPos; + memcpy(pArg, mData + mDataPos, sizeof(T)); mDataPos += sizeof(T); - *pArg = *reinterpret_cast<const T*>(data); return NO_ERROR; } else { return NOT_ENOUGH_DATA; @@ -1576,10 +1617,11 @@ T Parcel::readAligned() const { template<class T> status_t Parcel::writeAligned(T val) { static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); + static_assert(std::is_trivially_copyable_v<T>); if ((mDataPos+sizeof(val)) <= mDataCapacity) { restart_write: - *reinterpret_cast<T*>(mData+mDataPos) = val; + memcpy(mData + mDataPos, &val, sizeof(val)); return finishWrite(sizeof(val)); } @@ -1862,6 +1904,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..4a01d8176d 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -25,9 +25,10 @@ #include <binder/IServiceManager.h> #include <binder/Stability.h> #include <cutils/atomic.h> +#include <utils/AndroidThreads.h> #include <utils/Log.h> #include <utils/String8.h> -#include <utils/threads.h> +#include <utils/Thread.h> #include "Static.h" #include "binder_module.h" @@ -409,6 +410,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 +475,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..6d89064a5d 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; @@ -313,7 +313,8 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) { for (int i = 0; i < niovs; i++) { - LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(), + LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s", + what, i + 1, niovs, connection->rpcTransport.get(), android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } @@ -343,7 +344,8 @@ status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection, } for (int i = 0; i < niovs; i++) { - LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(), + LOG_RPC_DETAIL("Received %s (part %d of %d) on RpcTransport %p: %s", + what, i + 1, niovs, connection->rpcTransport.get(), android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } return OK; @@ -660,8 +662,14 @@ status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& con status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, CommandType type) { uint8_t buf; - while (connection->rpcTransport->peek(&buf, sizeof(buf)).value_or(0) > 0) { - status_t status = getAndExecuteCommand(connection, session, type); + while (true) { + size_t num_bytes; + status_t status = connection->rpcTransport->peek(&buf, sizeof(buf), &num_bytes); + if (status == WOULD_BLOCK) break; + if (status != OK) return status; + if (!num_bytes) break; + + status = getAndExecuteCommand(connection, session, type); if (status != OK) return status; } return OK; diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index 636e5d06e5..7cfc78082e 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -24,9 +24,6 @@ #include "FdTrigger.h" #include "RpcState.h" -using android::base::ErrnoError; -using android::base::Result; - namespace android { namespace { @@ -35,12 +32,20 @@ namespace { class RpcTransportRaw : public RpcTransport { public: explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {} - Result<size_t> peek(void *buf, size_t size) override { + status_t peek(void* buf, size_t size, size_t* out_size) override { ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK)); if (ret < 0) { - return ErrnoError() << "recv(MSG_PEEK)"; + int savedErrno = errno; + if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) { + return WOULD_BLOCK; + } + + LOG_RPC_DETAIL("RpcTransport peek(): %s", strerror(savedErrno)); + return -savedErrno; } - return ret; + + *out_size = static_cast<size_t>(ret); + return OK; } template <typename SendOrReceive> diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index 3936204bdf..bc68c379ce 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -37,10 +37,6 @@ #define LOG_TLS_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking #endif -using android::base::ErrnoError; -using android::base::Error; -using android::base::Result; - namespace android { namespace { @@ -165,17 +161,8 @@ public: return ret; } - // |sslError| should be from Ssl::getError(). - // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise - // return error. Also return error if |fdTrigger| is triggered before or during poll(). - status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger, - const char* fnString, int additionalEvent, - const std::function<status_t()>& altPoll) { + status_t toStatus(int sslError, const char* fnString) { switch (sslError) { - case SSL_ERROR_WANT_READ: - return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); - case SSL_ERROR_WANT_WRITE: - return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll); case SSL_ERROR_SYSCALL: { auto queue = toString(); LOG_TLS_DETAIL("%s(): %s. Treating as DEAD_OBJECT. Error queue: %s", fnString, @@ -191,6 +178,22 @@ public: } } + // |sslError| should be from Ssl::getError(). + // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise + // return error. Also return error if |fdTrigger| is triggered before or during poll(). + status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger, + const char* fnString, int additionalEvent, + const std::function<status_t()>& altPoll) { + switch (sslError) { + case SSL_ERROR_WANT_READ: + return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); + case SSL_ERROR_WANT_WRITE: + return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll); + default: + return toStatus(sslError, fnString); + } + } + private: bool mHandled = false; @@ -274,7 +277,7 @@ class RpcTransportTls : public RpcTransport { public: RpcTransportTls(android::base::unique_fd socket, Ssl ssl) : mSocket(std::move(socket)), mSsl(std::move(ssl)) {} - Result<size_t> peek(void* buf, size_t size) override; + status_t peek(void* buf, size_t size, size_t* out_size) override; status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) override; status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, @@ -286,7 +289,7 @@ private: }; // Error code is errno. -Result<size_t> RpcTransportTls::peek(void* buf, size_t size) { +status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_size) { size_t todo = std::min<size_t>(size, std::numeric_limits<int>::max()); auto [ret, errorQueue] = mSsl.call(SSL_peek, buf, static_cast<int>(todo)); if (ret < 0) { @@ -294,13 +297,15 @@ Result<size_t> RpcTransportTls::peek(void* buf, size_t size) { if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { // Seen EAGAIN / EWOULDBLOCK on recv(2) / send(2). // Like RpcTransportRaw::peek(), don't handle it here. - return Error(EWOULDBLOCK) << "SSL_peek(): " << errorQueue.toString(); + errorQueue.clear(); + return WOULD_BLOCK; } - return Error() << "SSL_peek(): " << errorQueue.toString(); + return errorQueue.toStatus(err, "SSL_peek"); } errorQueue.clear(); LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret); - return ret; + *out_size = static_cast<size_t>(ret); + return OK; } status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, diff --git a/libs/binder/Static.h b/libs/binder/Static.h index 83524e8575..8444fe780c 100644 --- a/libs/binder/Static.h +++ b/libs/binder/Static.h @@ -17,8 +17,6 @@ // All static variables go here, to control initialization and // destruction order in the library. -#include <utils/threads.h> - #include <binder/IBinder.h> #include <binder/ProcessState.h> diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index c0454b6fbd..19ad5e6efe 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -17,10 +17,9 @@ #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 +109,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/Parcel.h b/libs/binder/include/binder/Parcel.h index 450e3888f1..e2b2c5128d 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -95,6 +95,12 @@ public: bool hasFileDescriptors() const; status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const; + // returns all binder objects in the Parcel + std::vector<sp<IBinder>> debugReadAllStrongBinders() const; + // returns all file descriptors in the Parcel + // does not dup + std::vector<int> debugReadAllFileDescriptors() const; + // Zeros data when reallocating. Other mitigations may be added // in the future. // diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h index 42c85f9121..88790a803b 100644 --- a/libs/binder/include/binder/ParcelableHolder.h +++ b/libs/binder/include/binder/ParcelableHolder.h @@ -86,7 +86,7 @@ public: *ret = nullptr; return android::BAD_VALUE; } - *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + *ret = std::static_pointer_cast<T>(mParcelable); return android::OK; } this->mParcelPtr->setDataPosition(0); @@ -105,7 +105,7 @@ public: return status; } this->mParcelPtr = nullptr; - *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + *ret = std::static_pointer_cast<T>(mParcelable); return android::OK; } diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h index e65857417b..6f9eb5e4b9 100644 --- a/libs/binder/include/binder/PermissionController.h +++ b/libs/binder/include/binder/PermissionController.h @@ -19,8 +19,7 @@ #ifndef __ANDROID_VNDK__ #include <binder/IPermissionController.h> - -#include <utils/threads.h> +#include <utils/Mutex.h> // --------------------------------------------------------------------------- namespace android { diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index cf8d8e433c..675585ecef 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -18,11 +18,10 @@ #include <binder/IBinder.h> #include <utils/KeyedVector.h> +#include <utils/Mutex.h> #include <utils/String16.h> #include <utils/String8.h> -#include <utils/threads.h> - #include <pthread.h> // --------------------------------------------------------------------------- @@ -91,6 +90,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/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index aaa812b067..6b31812d17 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -77,7 +77,7 @@ public: * have */ [[nodiscard]] status_t setupInetServer(const char* address, unsigned int port, - unsigned int* assignedPort); + unsigned int* assignedPort = nullptr); /** * If setup*Server has been successful, return true. Otherwise return false. diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index ade2d94609..751c4f905d 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -22,7 +22,6 @@ #include <memory> #include <string> -#include <android-base/result.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> @@ -41,7 +40,7 @@ public: virtual ~RpcTransport() = default; // replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled. - [[nodiscard]] virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0; + [[nodiscard]] virtual status_t peek(void *buf, size_t size, size_t *out_size) = 0; /** * Read (or write), but allow to be interrupted by a trigger. diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h index b772b80227..abc7f1d374 100644 --- a/libs/binder/include_activitymanager/binder/ActivityManager.h +++ b/libs/binder/include_activitymanager/binder/ActivityManager.h @@ -18,10 +18,9 @@ #ifndef __ANDROID_VNDK__ -#include <binder/IActivityManager.h> #include <android/app/ProcessStateEnum.h> - -#include <utils/threads.h> +#include <binder/IActivityManager.h> +#include <utils/Mutex.h> // --------------------------------------------------------------------------- namespace android { 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_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h index 6880d86e1a..8e288b307e 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h @@ -56,6 +56,12 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en * If the binder is null, null is returned. If this binder object was originally an IBinder object, * the original java object will be returned. * + * WARNING: this function returns global and local references. This can be + * figured out using GetObjectRefType. Though, when this function is called + * from within a Java context, the local ref will automatically be cleaned + * up. If this is called outside of a Java frame, + * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup. + * * Available since API level 29. * * \param env Java environment. Must not be null. 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/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index f113ba8f21..d0cd11f6fc 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -97,6 +97,10 @@ static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) { * * This interface has system<->vendor stability */ +// b/227835797 - can't use __INTRODUCED_IN(30) because old targets load this code +#if __ANDROID_MIN_SDK_VERSION__ < 30 +__attribute__((weak)) +#endif // __ANDROID_MIN_SDK_VERSION__ < 30 void AIBinder_markVintfStability(AIBinder* binder); __END_DECLS diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 467e51e276..5d6206de0b 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -1086,7 +1086,7 @@ macro_rules! declare_binder_enum { { $( #[$attr:meta] )* $enum:ident : [$backing:ty; $size:expr] { - $( $name:ident = $value:expr, )* + $( $( #[$value_attr:meta] )* $name:ident = $value:expr, )* } } => { $( #[$attr] )* @@ -1094,7 +1094,7 @@ macro_rules! declare_binder_enum { #[allow(missing_docs)] pub struct $enum(pub $backing); impl $enum { - $( #[allow(missing_docs)] pub const $name: Self = Self($value); )* + $( $( #[$value_attr] )* #[allow(missing_docs)] pub const $name: Self = Self($value); )* #[inline(always)] #[allow(missing_docs)] 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/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index e3e47305d4..4df557bee0 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -575,6 +575,20 @@ struct DeathRecipientVtable { cookie_decr_refcount: unsafe extern "C" fn(*mut c_void), } +/// # Safety +/// +/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer +/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// `AIBinder_DeathRecipient` is threadsafe, this structure is too. +unsafe impl Send for DeathRecipient {} + +/// # Safety +/// +/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer +/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// `AIBinder_DeathRecipient` is threadsafe, this structure is too. +unsafe impl Sync for DeathRecipient {} + impl DeathRecipient { /// Create a new death recipient that will call the given callback when its /// associated object dies. diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp index ec780f28a1..3f59dab3a9 100644 --- a/libs/binder/rust/tests/serialization.cpp +++ b/libs/binder/rust/tests/serialization.cpp @@ -381,7 +381,7 @@ TEST_F(SerializationTest, SerializeFileDescriptor) { string expected = "TestingFileDescriptors"; vector<char> buf(expected.length()); base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size()); - ASSERT_EQ(expected, string(buf.data())); + ASSERT_EQ(expected, string(buf.data(), expected.length())); } TEST_F(SerializationTest, SerializeIBinder) { diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index b62da7be03..f6bdf5c9ed 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -117,8 +117,8 @@ fn on_transact( ) -> Result<(), StatusCode> { match code { bindings::Transaction_TEST_BOOL => { - assert_eq!(parcel.read::<bool>()?, true); - assert_eq!(parcel.read::<bool>()?, false); + assert!(parcel.read::<bool>()?); + assert!(!parcel.read::<bool>()?); assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { bindings::TESTDATA_BOOL }); 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/binderBinderUnitTest.cpp b/libs/binder/tests/binderBinderUnitTest.cpp index 1be0c59c78..ce2770f943 100644 --- a/libs/binder/tests/binderBinderUnitTest.cpp +++ b/libs/binder/tests/binderBinderUnitTest.cpp @@ -41,3 +41,10 @@ TEST(Binder, DetachObject) { EXPECT_EQ(kObject1, binder->detachObject(kObjectId1)); EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject2, nullptr, nullptr)); } + +TEST(Binder, AttachExtension) { + auto binder = sp<BBinder>::make(); + auto ext = sp<BBinder>::make(); + binder->setExtension(ext); + EXPECT_EQ(ext, binder->getExtension()); +} diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index b1e17b7892..57a1fda95f 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -247,13 +247,11 @@ class BinderLibTest : public ::testing::Test { { int32_t id; Parcel data, reply; - sp<IBinder> binder; EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR)); - EXPECT_FALSE(binder != nullptr); - binder = reply.readStrongBinder(); - EXPECT_TRUE(binder != nullptr); + sp<IBinder> binder = reply.readStrongBinder(); + EXPECT_NE(nullptr, binder); EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR)); if (idPtr) *idPtr = id; 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/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index aee15d8bd9..359c783de5 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp @@ -20,9 +20,12 @@ #include <cutils/ashmem.h> #include <gtest/gtest.h> +using android::BBinder; +using android::IBinder; using android::IPCThreadState; using android::OK; using android::Parcel; +using android::sp; using android::status_t; using android::String16; using android::String8; @@ -75,6 +78,40 @@ TEST(Parcel, EnforceNoDataAvail) { EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_NONE); } +TEST(Parcel, DebugReadAllBinders) { + sp<IBinder> binder1 = sp<BBinder>::make(); + sp<IBinder> binder2 = sp<BBinder>::make(); + + Parcel p; + p.writeInt32(4); + p.writeStrongBinder(binder1); + p.writeStrongBinder(nullptr); + p.writeInt32(4); + p.writeStrongBinder(binder2); + p.writeInt32(4); + + auto ret = p.debugReadAllStrongBinders(); + + ASSERT_EQ(ret.size(), 2); + EXPECT_EQ(ret[0], binder1); + EXPECT_EQ(ret[1], binder2); +} + +TEST(Parcel, DebugReadAllFds) { + Parcel p; + p.writeInt32(4); + p.writeFileDescriptor(STDOUT_FILENO, false /*takeOwnership*/); + p.writeInt32(4); + p.writeFileDescriptor(STDIN_FILENO, false /*takeOwnership*/); + p.writeInt32(4); + + auto ret = p.debugReadAllFileDescriptors(); + + ASSERT_EQ(ret.size(), 2); + EXPECT_EQ(ret[0], STDOUT_FILENO); + EXPECT_EQ(ret[1], STDIN_FILENO); +} + // Tests a second operation results in a parcel at the same location as it // started. void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) { diff --git a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h index 094addd690..50d12c408f 100644 --- a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h +++ b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h @@ -18,6 +18,7 @@ #include <memory> #include <mutex> +#include <vector> #include <binder/RpcAuth.h> #include <binder/RpcCertificateFormat.h> diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 38bde3a011..e5d32da4cc 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -29,7 +29,6 @@ cc_fuzz { "libcutils", "libhidlbase", "liblog", - "libutils", ], target: { @@ -37,12 +36,14 @@ cc_fuzz { shared_libs: [ "libbinder_ndk", "libbinder", + "libutils", ], }, host: { static_libs: [ "libbinder_ndk", "libbinder", + "libutils", ], }, darwin: { @@ -66,10 +67,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/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 13f7195eb7..47ec776990 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -109,6 +109,8 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { }, PARCEL_READ_NO_STATUS(size_t, allowFds), PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), + PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders), + PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors), [] (const ::android::Parcel& p, uint8_t len) { std::string interface(len, 'a'); FUZZ_LOG() << "about to enforceInterface: " << interface; 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/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h index 0a083d7665..843b6e3011 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h @@ -16,12 +16,13 @@ #pragma once +#include <android-base/unique_fd.h> #include <fuzzer/FuzzedDataProvider.h> namespace android { -// ownership to callee, always valid or aborts +// always valid or aborts // get a random FD for use in fuzzing, of a few different specific types -int getRandomFd(FuzzedDataProvider* provider); +base::unique_fd getRandomFd(FuzzedDataProvider* provider); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 749bf212e6..459fb127c2 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h @@ -19,13 +19,24 @@ #include <binder/Parcel.h> #include <fuzzer/FuzzedDataProvider.h> +#include <functional> +#include <vector> + namespace android { + +struct RandomParcelOptions { + std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader; + std::vector<sp<IBinder>> extraBinders; + std::vector<base::unique_fd> extraFds; +}; + /** * Fill parcel data, including some random binder objects and FDs + * + * p - the Parcel to fill + * provider - takes ownership and completely consumes provider + * writeHeader - optional function to write a specific header once the format of the parcel is + * picked (for instance, to write an interface header) */ -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider); -/** - * Fill parcel data, but don't fill any objects. - */ -void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider); +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {}); } // 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..d5aa353af0 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -0,0 +1,65 @@ +/* + * 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) { + sp<IBinder> target; + + RandomParcelOptions options{ + .extraBinders = {binder}, + .extraFds = {}, + }; + + while (provider.remaining_bytes() > 0) { + uint32_t code = provider.ConsumeIntegral<uint32_t>(); + uint32_t flags = provider.ConsumeIntegral<uint32_t>(); + Parcel data; + + sp<IBinder> target = options.extraBinders.at( + provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1)); + options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) { + // most code will be behind checks that the head of the Parcel + // is exactly this, so make it easier for fuzzers to reach this + if (provider.ConsumeBool()) { + p->writeInterfaceToken(target->getInterfaceDescriptor()); + } + }; + + std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>( + provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); + fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), options); + + Parcel reply; + (void)target->transact(code, data, &reply, flags); + + // feed back in binders and fds that are returned from the service, so that + // we can fuzz those binders, and use the fds and binders to feed back into + // the binders + auto retBinders = reply.debugReadAllStrongBinders(); + options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(), + retBinders.end()); + auto retFds = reply.debugReadAllFileDescriptors(); + for (size_t i = 0; i < retFds.size(); i++) { + options.extraFds.push_back(base::unique_fd(dup(retFds[i]))); + } + } +} + +} // 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/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp index cef6adb82d..ab0b7e306f 100644 --- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp @@ -23,13 +23,13 @@ namespace android { -int getRandomFd(FuzzedDataProvider* provider) { +base::unique_fd getRandomFd(FuzzedDataProvider* provider) { int fd = provider->PickValueInArray<std::function<int()>>({ []() { return ashmem_create_region("binder test region", 1024); }, []() { return open("/dev/null", O_RDWR); }, })(); CHECK(fd >= 0); - return fd; + return base::unique_fd(fd); } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 8bf04ccae0..0204f5ed61 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -34,15 +34,30 @@ private: String16 mDescriptor; }; -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { +static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { + std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes()); + CHECK(OK == p->write(data.data(), data.size())); +} + +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, + const RandomParcelOptions& options) { if (provider.ConsumeBool()) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); p->markForRpc(session); + + if (options.writeHeader) { + options.writeHeader(p, provider); + } + fillRandomParcelData(p, std::move(provider)); return; } + if (options.writeHeader) { + options.writeHeader(p, provider); + } + while (provider.remaining_bytes() > 0) { auto fillFunc = provider.PickValueInArray<const std::function<void()>>({ // write data @@ -54,8 +69,16 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { }, // write FD [&]() { - int fd = getRandomFd(&provider); - CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/)); + if (options.extraFds.size() > 0 && provider.ConsumeBool()) { + const base::unique_fd& fd = options.extraFds.at( + provider.ConsumeIntegralInRange<size_t>(0, + options.extraFds.size() - + 1)); + CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/)); + } else { + base::unique_fd fd = getRandomFd(&provider); + CHECK(OK == p->writeFileDescriptor(fd.release(), true /*takeOwnership*/)); + } }, // write binder [&]() { @@ -74,7 +97,15 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { // candidate for checking usage of an actual BpBinder return IInterface::asBinder(defaultServiceManager()); }, - []() { return nullptr; }, + [&]() -> sp<IBinder> { + if (options.extraBinders.size() > 0 && provider.ConsumeBool()) { + return options.extraBinders.at( + provider.ConsumeIntegralInRange< + size_t>(0, options.extraBinders.size() - 1)); + } else { + return nullptr; + } + }, }); sp<IBinder> binder = makeFunc(); CHECK(OK == p->writeStrongBinder(binder)); @@ -85,9 +116,4 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { } } -void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { - std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes()); - CHECK(OK == p->write(data.data(), data.size())); -} - } // namespace android diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h index 741987f754..5079431058 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h @@ -30,7 +30,6 @@ #include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/Mutex.h> -#include <utils/threads.h> #include <stdio.h> diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h index 4a0aebac43..bf7c61347f 100644 --- a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h @@ -27,7 +27,6 @@ #include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/Mutex.h> -#include <utils/threads.h> namespace android { diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 7e9bb7d468..d1690437ef 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -300,11 +300,11 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui } std::vector<tis_val_t> vals(gNCpus); - time_key_t key = {.uid = uid}; for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { - key.bucket = i; + const time_key_t key = {.uid = uid, .bucket = i}; if (findMapEntry(gTisMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {}; + time_key_t tmpKey; + if (errno != ENOENT || getFirstMapKey(gTisMapFd, &tmpKey)) return {}; continue; } @@ -412,10 +412,11 @@ std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)}; for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0); std::vector<concurrent_val_t> vals(gNCpus); - time_key_t key = {.uid = uid}; - for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + for (uint32_t i = 0; i <= (gNCpus - 1) / CPUS_PER_ENTRY; ++i) { + const time_key_t key = {.uid = uid, .bucket = i}; if (findMapEntry(gConcurrentMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {}; + time_key_t tmpKey; + if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &tmpKey)) return {}; continue; } auto offset = key.bucket * CPUS_PER_ENTRY; 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/ftl/Android.bp b/libs/ftl/Android.bp index 97626bec7d..2524c5f6d2 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -14,7 +14,9 @@ cc_test { address: true, }, srcs: [ + "Flags_test.cpp", "future_test.cpp", + "NamedEnum_test.cpp", "small_map_test.cpp", "small_vector_test.cpp", "static_vector_test.cpp", @@ -25,4 +27,12 @@ cc_test { "-Wextra", "-Wpedantic", ], + + header_libs: [ + "libbase_headers", + ], + + shared_libs: [ + "libbase", + ], } diff --git a/libs/input/tests/Flags_test.cpp b/libs/ftl/Flags_test.cpp index 6de030f7cf..8c00b5299b 100644 --- a/libs/input/tests/Flags_test.cpp +++ b/libs/ftl/Flags_test.cpp @@ -15,7 +15,7 @@ */ #include <gtest/gtest.h> -#include <input/Flags.h> +#include <ftl/Flags.h> #include <type_traits> diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp index 74a0044387..dff2b8aaa1 100644 --- a/libs/input/tests/NamedEnum_test.cpp +++ b/libs/ftl/NamedEnum_test.cpp @@ -15,7 +15,7 @@ */ #include <gtest/gtest.h> -#include <input/NamedEnum.h> +#include <ftl/NamedEnum.h> namespace android { diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS index 8c284647f2..347c4e0db1 100644 --- a/libs/graphicsenv/OWNERS +++ b/libs/graphicsenv/OWNERS @@ -1,4 +1,10 @@ +abdolrashidi@google.com +cclao@google.com chrisforbes@google.com cnorthrop@google.com +ianelliott@google.com +lfy@google.com lpy@google.com -timvp@google.com +romanl@google.com +vantablack@google.com +yuxinhu@google.com diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index fa853cb106..bd9abc1996 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -51,6 +51,60 @@ cc_library_headers { ], } +// AIDL files that should be exposed to java +filegroup { + name: "guiconstants_aidl", + srcs: [ + "android/**/DropInputMode.aidl", + "android/**/TouchOcclusionMode.aidl", + ], +} + +cc_library_static { + name: "libgui_window_info_static", + vendor_available: true, + host_supported: true, + srcs: [ + ":guiconstants_aidl", + "android/gui/FocusRequest.aidl", + "android/gui/InputApplicationInfo.aidl", + "android/gui/IWindowInfosListener.aidl", + "android/gui/IWindowInfosReportedListener.aidl", + "android/gui/WindowInfo.aidl", + "WindowInfo.cpp", + ], + + shared_libs: [ + "libbinder", + ], + + local_include_dirs: [ + "include", + ], + + export_shared_lib_headers: [ + "libbinder", + ], + + static_libs: [ + "libui-types", + ], + + aidl: { + export_aidl_headers: true + }, + + include_dirs: [ + "frameworks/native/include", + ], + + target: { + darwin: { + enabled: false, + }, + }, +} + filegroup { name: "libgui_aidl", srcs: ["aidl/**/*.aidl"], @@ -77,6 +131,10 @@ cc_library_static { "libbinder", ], + static_libs: [ + "libui-types", + ], + aidl: { export_aidl_headers: true, }, @@ -95,9 +153,11 @@ cc_library_shared { static_libs: [ "libgui_aidl_static", + "libgui_window_info_static", ], export_static_lib_headers: [ "libgui_aidl_static", + "libgui_window_info_static", ], srcs: [ @@ -138,6 +198,7 @@ cc_library_shared { "SyncFeatures.cpp", "TransactionTracing.cpp", "view/Surface.cpp", + "WindowInfosListenerReporter.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", "bufferqueue/2.0/B2HProducerListener.cpp", @@ -148,13 +209,11 @@ cc_library_shared { "libbinder", "libbufferhub", "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. - "libinput", "libpdx_default_transport", ], export_shared_lib_headers: [ "libbinder", - "libinput", ], export_header_lib_headers: [ @@ -166,7 +225,6 @@ cc_library_shared { vendor: { cflags: [ "-DNO_BUFFERHUB", - "-DNO_INPUT", ], exclude_srcs: [ "BufferHubConsumer.cpp", @@ -175,7 +233,6 @@ cc_library_shared { exclude_shared_libs: [ "libbufferhub", "libbufferhubqueue", - "libinput", "libpdx_default_transport", ], }, diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 9baf79b443..f034642681 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -38,7 +38,7 @@ using namespace std::chrono_literals; namespace { -inline const char* toString(bool b) { +inline const char* boolToString(bool b) { return b ? "true" : "false"; } } // namespace @@ -46,6 +46,8 @@ inline const char* toString(bool b) { namespace android { // Macros to include adapter info in log messages +#define BQA_LOGD(x, ...) \ + ALOGD("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) #define BQA_LOGV(x, ...) \ ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) // enable logs for a single layer @@ -125,12 +127,11 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } -BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, - int width, int height, int32_t format) - : mSurfaceControl(surface), - mSize(width, height), +BLASTBufferQueue::BLASTBufferQueue(const std::string& name) + : mSurfaceControl(nullptr), + mSize(1, 1), mRequestedSize(mSize), - mFormat(format), + mFormat(PIXEL_FORMAT_RGBA_8888), mNextTransaction(nullptr) { createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout @@ -151,23 +152,18 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont mBufferItemConsumer->setName(String8(consumerName.c_str())); mBufferItemConsumer->setFrameAvailableListener(this); mBufferItemConsumer->setBufferFreedListener(this); - mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height); - mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format)); ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); - - mTransformHint = mSurfaceControl->getTransformHint(); - mBufferItemConsumer->setTransformHint(mTransformHint); - SurfaceComposerClient::Transaction() - .setFlags(surface, layer_state_t::eEnableBackpressure, - layer_state_t::eEnableBackpressure) - .setApplyToken(mApplyToken) - .apply(); mNumAcquired = 0; mNumFrameAvailable = 0; - BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", width, - height, format, mTransformHint); + BQA_LOGV("BLASTBufferQueue created"); +} + +BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, + int width, int height, int32_t format) + : BLASTBufferQueue(name) { + update(surface, width, height, format); } BLASTBufferQueue::~BLASTBufferQueue() { @@ -218,12 +214,9 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, // If the buffer supports scaling, update the frame immediately since the client may // want to scale the existing buffer to the new size. mSize = mRequestedSize; - // We only need to update the scale if we've received at least one buffer. The reason - // for this is the scale is calculated based on the requested size and buffer size. - // If there's no buffer, the scale will always be 1. SurfaceComposerClient::Transaction* destFrameTransaction = (outTransaction) ? outTransaction : &t; - if (mSurfaceControl != nullptr && mLastBufferInfo.hasBuffer) { + if (mSurfaceControl != nullptr) { destFrameTransaction->setDestinationFrame(mSurfaceControl, Rect(0, 0, newSize.getWidth(), newSize.getHeight())); @@ -236,6 +229,67 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, } } +static std::optional<SurfaceControlStats> findMatchingStat( + const std::vector<SurfaceControlStats>& stats, const sp<SurfaceControl>& sc) { + for (auto stat : stats) { + if (SurfaceControl::isSameSurface(sc, stat.surfaceControl)) { + return stat; + } + } + return std::nullopt; +} + +static void transactionCommittedCallbackThunk(void* context, nsecs_t latchTime, + const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) { + if (context == nullptr) { + return; + } + sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context); + bq->transactionCommittedCallback(latchTime, presentFence, stats); +} + +void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, + const sp<Fence>& /*presentFence*/, + const std::vector<SurfaceControlStats>& stats) { + { + std::unique_lock _lock{mMutex}; + ATRACE_CALL(); + BQA_LOGV("transactionCommittedCallback"); + if (!mSurfaceControlsWithPendingCallback.empty()) { + sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front(); + std::optional<SurfaceControlStats> stat = findMatchingStat(stats, pendingSC); + if (stat) { + uint64_t currFrameNumber = stat->frameEventStats.frameNumber; + + // We need to check if we were waiting for a transaction callback in order to + // process any pending buffers and unblock. It's possible to get transaction + // callbacks for previous requests so we need to ensure the frame from this + // transaction callback matches the last acquired buffer. Since acquireNextBuffer + // will stop processing buffers when mWaitForTransactionCallback is set, we know + // that mLastAcquiredFrameNumber is the frame we're waiting on. + // We also want to check if mNextTransaction is null because it's possible another + // sync request came in while waiting, but it hasn't started processing yet. In that + // case, we don't actually want to flush the frames in between since they will get + // processed and merged with the sync transaction and released earlier than if they + // were sent to SF + if (mWaitForTransactionCallback && mNextTransaction == nullptr && + currFrameNumber >= mLastAcquiredFrameNumber) { + mWaitForTransactionCallback = false; + flushShadowQueueLocked(); + } + } else { + BQA_LOGE("Failed to find matching SurfaceControl in transactionCommittedCallback"); + } + } else { + BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " + "empty."); + } + + decStrong((void*)transactionCommittedCallbackThunk); + } +} + static void transactionCallbackThunk(void* context, nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats) { @@ -259,12 +313,9 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence if (!mSurfaceControlsWithPendingCallback.empty()) { sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front(); mSurfaceControlsWithPendingCallback.pop(); - bool found = false; - for (auto stat : stats) { - if (!SurfaceControl::isSameSurface(pendingSC, stat.surfaceControl)) { - continue; - } - + std::optional<SurfaceControlStats> statsOptional = findMatchingStat(stats, pendingSC); + if (statsOptional) { + SurfaceControlStats stat = *statsOptional; mTransformHint = stat.transformHint; mBufferItemConsumer->setTransformHint(mTransformHint); BQA_LOGV("updated mTransformHint=%d", mTransformHint); @@ -292,19 +343,25 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence transactionCompleteCallback = std::move(mTransactionCompleteCallback); mTransactionCompleteFrameNumber = 0; } - - found = true; - break; - } - - if (!found) { - BQA_LOGE("Failed to find matching SurfaceControl in transaction callback"); + std::vector<ReleaseCallbackId> staleReleases; + for (const auto& [key, value]: mSubmitted) { + if (currFrameNumber > key.framenumber) { + staleReleases.push_back(key); + } + } + for (const auto& staleRelease : staleReleases) { + releaseBufferCallbackLocked(staleRelease, stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE, + stat.transformHint, stat.currentMaxAcquiredBufferCount); + } + } else { + BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } } else { BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " "empty."); } + decStrong((void*)transactionCallbackThunk); } @@ -329,11 +386,31 @@ static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const Relea } } +void BLASTBufferQueue::flushShadowQueueLocked() { + BQA_LOGV("flushShadowQueueLocked"); + int numFramesToFlush = mNumFrameAvailable; + while (numFramesToFlush > 0) { + acquireNextBufferLocked(std::nullopt); + numFramesToFlush--; + } +} + +void BLASTBufferQueue::flushShadowQueue() { + std::unique_lock _lock{mMutex}; + flushShadowQueueLocked(); +} + void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) { - ATRACE_CALL(); std::unique_lock _lock{mMutex}; + releaseBufferCallbackLocked(id, releaseFence, transformHint, currentMaxAcquiredBufferCount); +} + +void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id, + const sp<Fence>& releaseFence, uint32_t transformHint, + uint32_t currentMaxAcquiredBufferCount) { + ATRACE_CALL(); BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); if (mSurfaceControl != nullptr) { @@ -354,7 +431,10 @@ void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id, const auto numPendingBuffersToHold = isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0; - mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence}); + auto rb = ReleasedBuffer{id, releaseFence}; + if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) { + mPendingRelease.emplace_back(rb); + } // Release all buffers that are beyond the ones that we need to hold while (mPendingRelease.size() > numPendingBuffersToHold) { @@ -370,7 +450,11 @@ void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id, BQA_LOGV("released %s", id.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence); mSubmitted.erase(it); - processNextBufferLocked(false /* useNextTransaction */); + // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let + // onFrameAvailable handle processing them since it will merge with the nextTransaction. + if (!mWaitForTransactionCallback) { + acquireNextBufferLocked(std::nullopt); + } } ATRACE_INT("PendingRelease", mPendingRelease.size()); @@ -379,13 +463,15 @@ void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id, mCallbackCV.notify_all(); } -void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { +void BLASTBufferQueue::acquireNextBufferLocked( + const std::optional<SurfaceComposerClient::Transaction*> transaction) { ATRACE_CALL(); // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't // include the extra buffer when checking if we can acquire the next buffer. - const bool includeExtraAcquire = !useNextTransaction; - if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) { - mCallbackCV.notify_all(); + const bool includeExtraAcquire = !transaction; + const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire); + if (mNumFrameAvailable == 0 || maxAcquired) { + BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired)); return; } @@ -397,9 +483,8 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { SurfaceComposerClient::Transaction localTransaction; bool applyTransaction = true; SurfaceComposerClient::Transaction* t = &localTransaction; - if (mNextTransaction != nullptr && useNextTransaction) { - t = mNextTransaction; - mNextTransaction = nullptr; + if (transaction) { + t = *transaction; applyTransaction = false; } @@ -429,7 +514,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform); mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); - processNextBufferLocked(useNextTransaction); + acquireNextBufferLocked(transaction); return; } @@ -449,10 +534,9 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); + const bool updateDestinationFrame = mRequestedSize != mSize; + mSize = mRequestedSize; Rect crop = computeCrop(bufferItem); - const bool updateDestinationFrame = - bufferItem.mScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE || - !mLastBufferInfo.hasBuffer; mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(), bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); @@ -468,6 +552,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { t->setAcquireFence(mSurfaceControl, bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); + mSurfaceControlsWithPendingCallback.push(mSurfaceControl); if (updateDestinationFrame) { @@ -520,10 +605,10 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { t->setApplyToken(mApplyToken).apply(); } - BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 + BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d" " graphicBufferId=%" PRIu64 "%s transform=%d", - mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction), + mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction), bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "", static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(), bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform); @@ -536,25 +621,75 @@ Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { return item.mCrop; } +void BLASTBufferQueue::acquireAndReleaseBuffer() { + BufferItem bufferItem; + status_t status = + mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); + if (status != OK) { + BQA_LOGE("Failed to acquire a buffer in acquireAndReleaseBuffer, err=%s", + statusToString(status).c_str()); + return; + } + mNumFrameAvailable--; + mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence); +} + void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; const bool nextTransactionSet = mNextTransaction != nullptr; + BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet)); if (nextTransactionSet) { - while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGV("waiting in onFrameAvailable..."); + if (mWaitForTransactionCallback) { + // We are waiting on a previous sync's transaction callback so allow another sync + // transaction to proceed. + // + // We need to first flush out the transactions that were in between the two syncs. + // We do this by merging them into mNextTransaction so any buffer merging will get + // a release callback invoked. The release callback will be async so we need to wait + // on max acquired to make sure we have the capacity to acquire another buffer. + if (maxBuffersAcquired(false /* includeExtraAcquire */)) { + BQA_LOGD("waiting to flush shadow queue..."); + mCallbackCV.wait(_lock); + } + while (mNumFrameAvailable > 0) { + // flush out the shadow queue + acquireAndReleaseBuffer(); + } + } + + while (maxBuffersAcquired(false /* includeExtraAcquire */)) { + BQA_LOGD("waiting for free buffer."); mCallbackCV.wait(_lock); } } + // add to shadow queue mNumFrameAvailable++; + if (mWaitForTransactionCallback && mNumFrameAvailable == 2) { + acquireAndReleaseBuffer(); + } ATRACE_INT(mQueuedBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber, - toString(nextTransactionSet)); - processNextBufferLocked(nextTransactionSet /* useNextTransaction */); + boolToString(nextTransactionSet)); + + if (nextTransactionSet) { + acquireNextBufferLocked(std::move(mNextTransaction)); + + // Only need a commit callback when syncing to ensure the buffer that's synced has been sent + // to SF + incStrong((void*)transactionCommittedCallbackThunk); + mNextTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, + static_cast<void*>(this)); + + mNextTransaction = nullptr; + mWaitForTransactionCallback = true; + } else if (!mWaitForTransactionCallback) { + acquireNextBufferLocked(std::nullopt); + } } void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) { @@ -579,7 +714,6 @@ void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) { - mSize = mRequestedSize; // Only reject buffers if scaling mode is freeze. return false; } @@ -593,7 +727,6 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { } ui::Size bufferSize(bufWidth, bufHeight); if (mRequestedSize != mSize && mRequestedSize == bufferSize) { - mSize = mRequestedSize; return false; } @@ -861,4 +994,9 @@ uint32_t BLASTBufferQueue::getLastTransformHint() const { } } +uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { + std::unique_lock _lock{mMutex}; + return mLastAcquiredFrameNumber; +} + } // namespace android diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 5023b6bb81..2930154ad4 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -34,7 +34,6 @@ #include <gui/BufferQueueCore.h> #include <gui/IConsumerListener.h> #include <gui/IProducerListener.h> -#include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> #include <system/window.h> diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index a82fc6fa81..1726761785 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -44,6 +44,7 @@ namespace android { +using gui::IWindowInfosListener; using ui::ColorMode; class BpSurfaceComposer : public BpInterface<ISurfaceComposer> @@ -1238,6 +1239,22 @@ public: return reply.readInt32(buffers); } + + status_t addWindowInfosListener( + const sp<IWindowInfosListener>& windowInfosListener) const override { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener)); + return remote()->transact(BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER, data, &reply); + } + + status_t removeWindowInfosListener( + const sp<IWindowInfosListener>& windowInfosListener) const override { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener)); + return remote()->transact(BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, data, &reply); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -2128,6 +2145,20 @@ status_t BnSurfaceComposer::onTransact( SAFE_PARCEL(reply->writeBool, success); return err; } + case ADD_WINDOW_INFOS_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IWindowInfosListener> listener; + SAFE_PARCEL(data.readStrongBinder, &listener); + + return addWindowInfosListener(listener); + } + case REMOVE_WINDOW_INFOS_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IWindowInfosListener> listener; + SAFE_PARCEL(data.readStrongBinder, &listener); + + return removeWindowInfosListener(listener); + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 076c90dd23..77a883b332 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -31,6 +31,9 @@ namespace android { +using gui::FocusRequest; +using gui::WindowInfoHandle; + layer_state_t::layer_state_t() : what(0), x(0), @@ -67,7 +70,8 @@ layer_state_t::layer_state_t() isTrustedOverlay(false), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), - releaseBufferListener(nullptr) { + releaseBufferListener(nullptr), + dropInputMode(gui::DropInputMode::NONE) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; hdrMetadata.validTypes = 0; @@ -95,9 +99,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, color.r); SAFE_PARCEL(output.writeFloat, color.g); SAFE_PARCEL(output.writeFloat, color.b); -#ifndef NO_INPUT - SAFE_PARCEL(inputHandle->writeToParcel, &output); -#endif + SAFE_PARCEL(windowInfoHandle->writeToParcel, &output); SAFE_PARCEL(output.write, transparentRegion); SAFE_PARCEL(output.writeUint32, transform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); @@ -173,6 +175,8 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, destinationFrame); SAFE_PARCEL(output.writeBool, isTrustedOverlay); + SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint); + SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode)); return NO_ERROR; } @@ -207,9 +211,7 @@ status_t layer_state_t::read(const Parcel& input) color.g = tmpFloat; SAFE_PARCEL(input.readFloat, &tmpFloat); color.b = tmpFloat; -#ifndef NO_INPUT - SAFE_PARCEL(inputHandle->readFromParcel, &input); -#endif + SAFE_PARCEL(windowInfoHandle->readFromParcel, &input); SAFE_PARCEL(input.read, transparentRegion); SAFE_PARCEL(input.readUint32, &transform); @@ -304,6 +306,11 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, destinationFrame); SAFE_PARCEL(input.readBool, &isTrustedOverlay); + SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint); + + uint32_t mode; + SAFE_PARCEL(input.readUint32, &mode); + dropInputMode = static_cast<gui::DropInputMode>(mode); return NO_ERROR; } @@ -318,6 +325,7 @@ status_t ComposerState::read(const Parcel& input) { DisplayState::DisplayState() : what(0), layerStack(0), + flags(0), layerStackSpaceRect(Rect::EMPTY_RECT), orientedDisplaySpaceRect(Rect::EMPTY_RECT), width(0), @@ -328,6 +336,7 @@ status_t DisplayState::write(Parcel& output) const { SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface)); SAFE_PARCEL(output.writeUint32, what); SAFE_PARCEL(output.writeUint32, layerStack); + SAFE_PARCEL(output.writeUint32, flags); SAFE_PARCEL(output.writeUint32, toRotationInt(orientation)); SAFE_PARCEL(output.write, layerStackSpaceRect); SAFE_PARCEL(output.write, orientedDisplaySpaceRect); @@ -344,6 +353,7 @@ status_t DisplayState::read(const Parcel& input) { SAFE_PARCEL(input.readUint32, &what); SAFE_PARCEL(input.readUint32, &layerStack); + SAFE_PARCEL(input.readUint32, &flags); uint32_t tmpUint = 0; SAFE_PARCEL(input.readUint32, &tmpUint); orientation = ui::toRotation(tmpUint); @@ -364,6 +374,10 @@ void DisplayState::merge(const DisplayState& other) { what |= eLayerStackChanged; layerStack = other.layerStack; } + if (other.what & eFlagsChanged) { + what |= eFlagsChanged; + flags = other.flags; + } if (other.what & eDisplayProjectionChanged) { what |= eDisplayProjectionChanged; orientation = other.orientation; @@ -455,6 +469,7 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eBufferChanged) { what |= eBufferChanged; buffer = other.buffer; + releaseBufferEndpoint = other.releaseBufferEndpoint; } if (other.what & eAcquireFenceChanged) { what |= eAcquireFenceChanged; @@ -487,17 +502,14 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eHasListenerCallbacksChanged) { what |= eHasListenerCallbacksChanged; } - -#ifndef NO_INPUT if (other.what & eInputInfoChanged) { what |= eInputInfoChanged; - inputHandle = new InputWindowHandle(*other.inputHandle); + windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle); } -#endif - if (other.what & eCachedBufferChanged) { what |= eCachedBufferChanged; cachedBuffer = other.cachedBuffer; + releaseBufferEndpoint = other.releaseBufferEndpoint; } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; @@ -558,6 +570,14 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eDestinationFrameChanged; destinationFrame = other.destinationFrame; } + if (other.what & eDropInputModeChanged) { + what |= eDropInputModeChanged; + dropInputMode = other.dropInputMode; + } + if (other.what & eColorChanged) { + what |= eColorChanged; + color = other.color; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, @@ -593,11 +613,9 @@ status_t layer_state_t::matrix22_t::read(const Parcel& input) { bool InputWindowCommands::merge(const InputWindowCommands& other) { bool changes = false; -#ifndef NO_INPUT changes |= !other.focusRequests.empty(); focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()), std::make_move_iterator(other.focusRequests.end())); -#endif changes |= other.syncInputWindows && !syncInputWindows; syncInputWindows |= other.syncInputWindows; return changes; @@ -605,31 +623,23 @@ bool InputWindowCommands::merge(const InputWindowCommands& other) { bool InputWindowCommands::empty() const { bool empty = true; -#ifndef NO_INPUT empty = focusRequests.empty() && !syncInputWindows; -#endif return empty; } void InputWindowCommands::clear() { -#ifndef NO_INPUT focusRequests.clear(); -#endif syncInputWindows = false; } status_t InputWindowCommands::write(Parcel& output) const { -#ifndef NO_INPUT SAFE_PARCEL(output.writeParcelableVector, focusRequests); -#endif SAFE_PARCEL(output.writeBool, syncInputWindows); return NO_ERROR; } status_t InputWindowCommands::read(const Parcel& input) { -#ifndef NO_INPUT SAFE_PARCEL(input.readParcelableVector, &focusRequests); -#endif SAFE_PARCEL(input.readBool, &syncInputWindows); return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 1377284f92..725ea6571d 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -19,6 +19,7 @@ #include <stdint.h> #include <sys/types.h> +#include <android/gui/IWindowInfosListener.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/SortedVector.h> @@ -39,14 +40,11 @@ #include <gui/LayerState.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#include <gui/WindowInfo.h> #include <private/gui/ParcelUtils.h> #include <ui/DisplayMode.h> #include <ui/DynamicDisplayInfo.h> -#ifndef NO_INPUT -#include <input/InputWindow.h> -#endif - #include <private/gui/ComposerService.h> // This server size should always be smaller than the server cache size @@ -54,6 +52,10 @@ namespace android { +using gui::FocusRequest; +using gui::WindowInfo; +using gui::WindowInfoHandle; +using gui::WindowInfosListener; using ui::ColorMode; // --------------------------------------------------------------------------- @@ -95,6 +97,7 @@ bool ComposerService::connectLocked() { if (instance.mComposerService == nullptr) { if (ComposerService::getInstance().connectLocked()) { ALOGD("ComposerService reconnected"); + WindowInfosListenerReporter::getInstance()->reconnect(instance.mComposerService); } } return instance.mComposerService; @@ -144,8 +147,16 @@ int64_t TransactionCompletedListener::getNextIdLocked() { return mCallbackIdCounter++; } +sp<TransactionCompletedListener> TransactionCompletedListener::sInstance = nullptr; + +void TransactionCompletedListener::setInstance(const sp<TransactionCompletedListener>& listener) { + sInstance = listener; +} + sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() { - static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener; + if (sInstance == nullptr) { + sInstance = new TransactionCompletedListener; + } return sInstance; } @@ -183,7 +194,7 @@ CallbackId TransactionCompletedListener::addCallbackFunction( void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener, sp<SurfaceControl> surfaceControl) { std::lock_guard<std::mutex> lock(mMutex); - mJankListeners.insert({surfaceControl->getHandle(), listener}); + mJankListeners.insert({surfaceControl->getLayerId(), listener}); } void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) { @@ -212,8 +223,8 @@ void TransactionCompletedListener::removeReleaseBufferCallback( void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie, sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) { std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex); - mSurfaceStatsListeners.insert({surfaceControl->getHandle(), - SurfaceStatsCallbackEntry(context, cookie, listener)}); + mSurfaceStatsListeners.insert( + {surfaceControl->getLayerId(), SurfaceStatsCallbackEntry(context, cookie, listener)}); } void TransactionCompletedListener::removeSurfaceStatsListener(void* context, void* cookie) { @@ -243,7 +254,7 @@ void TransactionCompletedListener::addSurfaceControlToCallbacks( void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; - std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap; + std::multimap<int32_t, sp<JankDataListener>> jankListenersMap; { std::lock_guard<std::mutex> lock(mMutex); @@ -285,7 +296,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener transactionStats.latchTime, surfaceStats.acquireTime, transactionStats.presentFence, surfaceStats.previousReleaseFence, surfaceStats.transformHint, - surfaceStats.eventStats); + surfaceStats.eventStats, surfaceStats.currentMaxAcquiredBufferCount); } callbackFunction(transactionStats.latchTime, transactionStats.presentFence, @@ -310,7 +321,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener transactionStats.latchTime, surfaceStats.acquireTime, transactionStats.presentFence, surfaceStats.previousReleaseFence, surfaceStats.transformHint, - surfaceStats.eventStats); + surfaceStats.eventStats, surfaceStats.currentMaxAcquiredBufferCount); if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) { callbacksMap[callbackId] .surfaceControls[surfaceStats.surfaceControl] @@ -341,13 +352,26 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener callbackFunction(transactionStats.latchTime, transactionStats.presentFence, surfaceControlStats); } + for (const auto& surfaceStats : transactionStats.surfaceStats) { + // The callbackMap contains the SurfaceControl object, which we need to look up the + // layerId. Since we don't know which callback contains the SurfaceControl, iterate + // through all until the SC is found. + int32_t layerId = -1; + for (auto callbackId : transactionStats.callbackIds) { + sp<SurfaceControl> sc = + callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]; + if (sc != nullptr) { + layerId = sc->getLayerId(); + break; + } + } + { // Acquire surface stats listener lock such that we guarantee that after calling // unregister, there won't be any further callback. std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex); - auto listenerRange = mSurfaceStatsListeners.equal_range( - surfaceStats.surfaceControl); + auto listenerRange = mSurfaceStatsListeners.equal_range(layerId); for (auto it = listenerRange.first; it != listenerRange.second; it++) { auto entry = it->second; entry.callback(entry.context, transactionStats.latchTime, @@ -356,7 +380,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener } if (surfaceStats.jankData.empty()) continue; - auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl); + auto jankRange = jankListenersMap.equal_range(layerId); for (auto it = jankRange.first; it != jankRange.second; it++) { it->second->onJankDataAvailable(surfaceStats.jankData); } @@ -1275,6 +1299,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe removeReleaseBufferCallback(s); s->what |= layer_state_t::eBufferChanged; s->buffer = buffer; + s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance()); if (mIsAutoTimestamp) { mDesiredPresentTime = systemTime(); } @@ -1495,16 +1520,14 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame return *this; } -#ifndef NO_INPUT SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( - const sp<SurfaceControl>& sc, - const InputWindowInfo& info) { + const sp<SurfaceControl>& sc, const WindowInfo& info) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->inputHandle = new InputWindowHandle(info); + s->windowInfoHandle = new WindowInfoHandle(info); s->what |= layer_state_t::eInputInfoChanged; return *this; } @@ -1520,8 +1543,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInpu return *this; } -#endif - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform( const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation) { layer_state_t* s = getLayerState(sc); @@ -1722,6 +1743,21 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesti return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropInputMode( + const sp<SurfaceControl>& sc, gui::DropInputMode mode) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eDropInputModeChanged; + s->dropInputMode = mode; + + registerSurfaceControlForCallback(sc); + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -1762,6 +1798,12 @@ void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& s.what |= DisplayState::eLayerStackChanged; } +void SurfaceComposerClient::Transaction::setDisplayFlags(const sp<IBinder>& token, uint32_t flags) { + DisplayState& s(getDisplayState(token)); + s.flags = flags; + s.what |= DisplayState::eFlagsChanged; +} + void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation, const Rect& layerStackRect, @@ -1783,15 +1825,10 @@ void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token // --------------------------------------------------------------------------- -SurfaceComposerClient::SurfaceComposerClient() - : mStatus(NO_INIT) -{ -} +SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT) {} SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& client) - : mStatus(NO_ERROR), mClient(client) -{ -} + : mStatus(NO_ERROR), mClient(client) {} void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); @@ -2157,6 +2194,18 @@ int SurfaceComposerClient::getGPUContextPriority() { return ComposerService::getComposerService()->getGPUContextPriority(); } +status_t SurfaceComposerClient::addWindowInfosListener( + const sp<WindowInfosListener>& windowInfosListener) { + return WindowInfosListenerReporter::getInstance() + ->addWindowInfosListener(windowInfosListener, ComposerService::getComposerService()); +} + +status_t SurfaceComposerClient::removeWindowInfosListener( + const sp<WindowInfosListener>& windowInfosListener) { + return WindowInfosListenerReporter::getInstance() + ->removeWindowInfosListener(windowInfosListener, ComposerService::getComposerService()); +} + // ---------------------------------------------------------------------------- status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, diff --git a/libs/input/InputWindow.cpp b/libs/gui/WindowInfo.cpp index 99477200db..b2ef7aabc9 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/gui/WindowInfo.cpp @@ -15,50 +15,47 @@ */ #include <type_traits> -#define LOG_TAG "InputWindow" +#define LOG_TAG "WindowInfo" #define LOG_NDEBUG 0 #include <android-base/stringprintf.h> #include <binder/Parcel.h> -#include <input/InputTransport.h> -#include <input/InputWindow.h> +#include <gui/WindowInfo.h> #include <log/log.h> -namespace android { +namespace android::gui { - -// --- InputWindowInfo --- -void InputWindowInfo::addTouchableRegion(const Rect& region) { +// --- WindowInfo --- +void WindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); } -bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { - return touchableRegion.contains(x,y); +bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { + return touchableRegion.contains(x, y); } -bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { - return x >= frameLeft && x < frameRight - && y >= frameTop && y < frameBottom; +bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const { + return x >= frameLeft && x < frameRight && y >= frameTop && y < frameBottom; } -bool InputWindowInfo::supportsSplitTouch() const { +bool WindowInfo::supportsSplitTouch() const { return flags.test(Flag::SPLIT_TOUCH); } -bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { - return frameLeft < other->frameRight && frameRight > other->frameLeft - && frameTop < other->frameBottom && frameBottom > other->frameTop; +bool WindowInfo::overlaps(const WindowInfo* other) const { + return frameLeft < other->frameRight && frameRight > other->frameLeft && + frameTop < other->frameBottom && frameBottom > other->frameTop; } -bool InputWindowInfo::operator==(const InputWindowInfo& info) const { +bool WindowInfo::operator==(const WindowInfo& info) const { return info.token == token && info.id == id && info.name == name && info.flags == flags && info.type == type && info.dispatchingTimeout == dispatchingTimeout && info.frameLeft == frameLeft && info.frameTop == frameTop && info.frameRight == frameRight && info.frameBottom == frameBottom && info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor && - info.transform == transform && info.displayWidth == displayWidth && - info.displayHeight == displayHeight && + info.transform == transform && info.displayOrientation == displayOrientation && + info.displayWidth == displayWidth && info.displayHeight == displayHeight && info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible && info.trustedOverlay == trustedOverlay && info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper && @@ -69,7 +66,7 @@ bool InputWindowInfo::operator==(const InputWindowInfo& info) const { info.applicationInfo == applicationInfo; } -status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const { +status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { if (parcel == nullptr) { ALOGE("%s: Null parcel", __func__); return BAD_VALUE; @@ -86,7 +83,7 @@ status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeInt32(id) ?: parcel->writeUtf8AsUtf16(name) ?: parcel->writeInt32(flags.get()) ?: - parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?: + parcel->writeInt32(static_cast<std::underlying_type_t<WindowInfo::Type>>(type)) ?: parcel->writeInt32(frameLeft) ?: parcel->writeInt32(frameTop) ?: parcel->writeInt32(frameRight) ?: @@ -100,6 +97,7 @@ status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeFloat(transform.dtdy()) ?: parcel->writeFloat(transform.dsdy()) ?: parcel->writeFloat(transform.ty()) ?: + parcel->writeUint32(displayOrientation) ?: parcel->writeInt32(displayWidth) ?: parcel->writeInt32(displayHeight) ?: parcel->writeBool(visible) ?: @@ -117,12 +115,13 @@ status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const { applicationInfo.writeToParcel(parcel) ?: parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: - parcel->writeStrongBinder(touchableRegionCropHandle.promote()); + parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: + parcel->writeStrongBinder(windowToken); // clang-format on return status; } -status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) { +status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { if (parcel == nullptr) { ALOGE("%s: Null parcel", __func__); return BAD_VALUE; @@ -156,6 +155,7 @@ status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readFloat(&dtdy) ?: parcel->readFloat(&dsdy) ?: parcel->readFloat(&ty) ?: + parcel->readUint32(&displayOrientation) ?: parcel->readInt32(&displayWidth) ?: parcel->readInt32(&displayHeight) ?: parcel->readBool(&visible) ?: @@ -176,11 +176,13 @@ status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) { touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt); inputFeatures = Flags<Feature>(parcel->readInt32()); + // clang-format off status = parcel->readInt32(&displayId) ?: parcel->readInt32(&portalToDisplayId) ?: applicationInfo.readFromParcel(parcel) ?: parcel->read(touchableRegion) ?: parcel->readBool(&replaceTouchableRegionWithCrop); + // clang-format on if (status != OK) { return status; @@ -189,36 +191,37 @@ status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) { touchableRegionCropHandle = parcel->readStrongBinder(); transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); - return OK; + status = parcel->readNullableStrongBinder(&windowToken); + return status; } -// --- InputWindowHandle --- +// --- WindowInfoHandle --- -InputWindowHandle::InputWindowHandle() {} +WindowInfoHandle::WindowInfoHandle() {} -InputWindowHandle::~InputWindowHandle() {} +WindowInfoHandle::~WindowInfoHandle() {} -InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {} +WindowInfoHandle::WindowInfoHandle(const WindowInfoHandle& other) : mInfo(other.mInfo) {} -InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {} +WindowInfoHandle::WindowInfoHandle(const WindowInfo& other) : mInfo(other) {} -status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const { +status_t WindowInfoHandle::writeToParcel(android::Parcel* parcel) const { return mInfo.writeToParcel(parcel); } -status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) { +status_t WindowInfoHandle::readFromParcel(const android::Parcel* parcel) { return mInfo.readFromParcel(parcel); } -void InputWindowHandle::releaseChannel() { +void WindowInfoHandle::releaseChannel() { mInfo.token.clear(); } -sp<IBinder> InputWindowHandle::getToken() const { +sp<IBinder> WindowInfoHandle::getToken() const { return mInfo.token; } -void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) { +void WindowInfoHandle::updateFrom(sp<WindowInfoHandle> handle) { mInfo = handle->mInfo; } -} // namespace android +} // namespace android::gui diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp new file mode 100644 index 0000000000..c00a4389ad --- /dev/null +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/ISurfaceComposer.h> +#include <gui/WindowInfosListenerReporter.h> + +namespace android { + +using gui::IWindowInfosReportedListener; +using gui::WindowInfo; +using gui::WindowInfosListener; + +sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() { + static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter; + return sInstance; +} + +status_t WindowInfosListenerReporter::addWindowInfosListener( + const sp<WindowInfosListener>& windowInfosListener, + const sp<ISurfaceComposer>& surfaceComposer) { + status_t status = OK; + { + std::scoped_lock lock(mListenersMutex); + if (mWindowInfosListeners.empty()) { + status = surfaceComposer->addWindowInfosListener(this); + } + + if (status == OK) { + mWindowInfosListeners.insert(windowInfosListener); + } + } + + return status; +} + +status_t WindowInfosListenerReporter::removeWindowInfosListener( + const sp<WindowInfosListener>& windowInfosListener, + const sp<ISurfaceComposer>& surfaceComposer) { + status_t status = OK; + { + std::scoped_lock lock(mListenersMutex); + if (mWindowInfosListeners.size() == 1) { + status = surfaceComposer->removeWindowInfosListener(this); + } + + if (status == OK) { + mWindowInfosListeners.erase(windowInfosListener); + } + } + + return status; +} + +binder::Status WindowInfosListenerReporter::onWindowInfosChanged( + const std::vector<WindowInfo>& windowInfos, + const sp<IWindowInfosReportedListener>& windowInfosReportedListener) { + std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>> + windowInfosListeners; + + { + std::scoped_lock lock(mListenersMutex); + for (auto listener : mWindowInfosListeners) { + windowInfosListeners.insert(listener); + } + } + + for (auto listener : windowInfosListeners) { + listener->onWindowInfosChanged(windowInfos); + } + + if (windowInfosReportedListener) { + windowInfosReportedListener->onWindowInfosReported(); + } + + return binder::Status::ok(); +} + +void WindowInfosListenerReporter::reconnect(const sp<ISurfaceComposer>& composerService) { + std::scoped_lock lock(mListenersMutex); + if (!mWindowInfosListeners.empty()) { + composerService->addWindowInfosListener(this); + } +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/android/gui/DropInputMode.aidl b/libs/gui/android/gui/DropInputMode.aidl new file mode 100644 index 0000000000..2b31744ca2 --- /dev/null +++ b/libs/gui/android/gui/DropInputMode.aidl @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + + +/** + * Input event drop modes: Input event drop options for windows and its children. + * + * @hide + */ +@Backing(type="int") +enum DropInputMode { + /** + * Default mode, input events are sent to the target as usual. + */ + NONE, + + /** + * Window and its children will not receive any input even if it has a valid input channel. + * Touches and keys will be dropped. If a window is focused, it will remain focused but will + * not receive any keys. If the window has a touchable region and is the target of an input + * event, the event will be dropped and will not go to the window behind. ref: b/197296414 + */ + ALL, + + /** + * Similar to DROP but input events are only dropped if the window is considered to be + * obscured. ref: b/197364677 + */ + OBSCURED +} diff --git a/libs/input/android/FocusRequest.aidl b/libs/gui/android/gui/FocusRequest.aidl index 8812d34a72..90186351c5 100644 --- a/libs/input/android/FocusRequest.aidl +++ b/libs/gui/android/gui/FocusRequest.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android; +package android.gui; /** @hide */ parcelable FocusRequest { diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl new file mode 100644 index 0000000000..d4553ca82d --- /dev/null +++ b/libs/gui/android/gui/IWindowInfosListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.IWindowInfosReportedListener; +import android.gui.WindowInfo; + +/** @hide */ +oneway interface IWindowInfosListener +{ + void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener); +} diff --git a/libs/input/android/os/ISetInputWindowsListener.aidl b/libs/gui/android/gui/IWindowInfosReportedListener.aidl index bb58fb671b..0e4cce61a2 100644 --- a/libs/input/android/os/ISetInputWindowsListener.aidl +++ b/libs/gui/android/gui/IWindowInfosReportedListener.aidl @@ -14,10 +14,10 @@ * limitations under the License. */ -package android.os; +package android.gui; /** @hide */ -oneway interface ISetInputWindowsListener +oneway interface IWindowInfosReportedListener { - void onSetInputWindowsFinished(); + void onWindowInfosReported(); } diff --git a/libs/input/android/InputApplicationInfo.aidl b/libs/gui/android/gui/InputApplicationInfo.aidl index 933603916d..c0fd666543 100644 --- a/libs/input/android/InputApplicationInfo.aidl +++ b/libs/gui/android/gui/InputApplicationInfo.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android; +package android.gui; parcelable InputApplicationInfo { @nullable IBinder token; diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/gui/android/gui/TouchOcclusionMode.aidl index 106f159a50..d91d052135 100644 --- a/libs/input/android/os/TouchOcclusionMode.aidl +++ b/libs/gui/android/gui/TouchOcclusionMode.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.os; +package android.gui; /** diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/gui/android/gui/WindowInfo.aidl index eeaf400227..2c85d155a8 100644 --- a/libs/input/android/InputWindowInfo.aidl +++ b/libs/gui/android/gui/WindowInfo.aidl @@ -1,5 +1,4 @@ -/* //device/java/android/android/view/InputChannel.aidl -** +/* ** Copyright 2020, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,6 +14,6 @@ ** limitations under the License. */ -package android; +package android.gui; -parcelable InputWindowInfo cpp_header "input/InputWindow.h"; +parcelable WindowInfo cpp_header "gui/WindowInfo.h"; diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index bc35792bef..f9e40ecb5b 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -53,7 +53,6 @@ public: CompositorTiming compositorTiming, nsecs_t latchTime, nsecs_t dequeueReadyTime) REQUIRES(mMutex); void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect); - void setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) REQUIRES(mMutex); protected: void onSidebandStreamChanged() override REQUIRES(mMutex); @@ -74,6 +73,7 @@ class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener { public: + BLASTBufferQueue(const std::string& name); BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, int height, int32_t format); @@ -88,10 +88,14 @@ public: void onFrameDequeued(const uint64_t) override; void onFrameCancelled(const uint64_t) override; + void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats); void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount); + void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, + uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount); void setNextTransaction(SurfaceComposerClient::Transaction *t); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void setTransactionCompleteCallback(uint64_t frameNumber, @@ -99,7 +103,6 @@ public: void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format, SurfaceComposerClient::Transaction* outTransaction = nullptr); - void flushShadowQueue() {} status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); status_t setFrameTimelineInfo(const FrameTimelineInfo& info); @@ -107,6 +110,9 @@ public: void setSidebandStream(const sp<NativeHandle>& stream); uint32_t getLastTransformHint() const; + void flushShadowQueue(); + + uint64_t getLastAcquiredFrameNum(); virtual ~BLASTBufferQueue(); @@ -119,13 +125,17 @@ private: void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer); - void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex); + void acquireNextBufferLocked( + const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); // Return true if we need to reject the buffer based on the scaling mode and the buffer size. bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex); static PixelFormat convertBufferFormat(PixelFormat& format); + void flushShadowQueueLocked() REQUIRES(mMutex); + void acquireAndReleaseBuffer() REQUIRES(mMutex); + std::string mName; // Represents the queued buffer count from buffer queue, // pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) + @@ -155,6 +165,12 @@ private: struct ReleasedBuffer { ReleaseCallbackId callbackId; sp<Fence> releaseFence; + bool operator==(const ReleasedBuffer& rhs) const { + // Only compare Id so if we somehow got two callbacks + // with different fences we don't decrement mNumAcquired + // too far. + return rhs.callbackId == callbackId; + } }; std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex); @@ -233,6 +249,9 @@ private: // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a // callback for them. std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); + + uint32_t mCurrentMaxAcquiredBufferCount; + bool mWaitForTransactionCallback = false; }; } // namespace android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index ef5353f594..cd289cb401 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -22,11 +22,12 @@ #include <android/gui/IScreenCaptureListener.h> #include <android/gui/ITransactionTraceListener.h> #include <android/gui/ITunnelModeEnabledListener.h> +#include <android/gui/IWindowInfosListener.h> #include <binder/IBinder.h> #include <binder/IInterface.h> +#include <ftl/Flags.h> #include <gui/FrameTimelineInfo.h> #include <gui/ITransactionCompletedListener.h> -#include <input/Flags.h> #include <math/vec4.h> #include <stdint.h> #include <sys/types.h> @@ -554,6 +555,11 @@ public: * in MIN_UNDEQUEUED_BUFFERS. */ virtual status_t getMaxAcquiredBufferCount(int* buffers) const = 0; + + virtual status_t addWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0; + virtual status_t removeWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0; }; // ---------------------------------------------------------------------------- @@ -626,6 +632,8 @@ public: ON_PULL_ATOM, ADD_TUNNEL_MODE_ENABLED_LISTENER, REMOVE_TUNNEL_MODE_ENABLED_LISTENER, + ADD_WINDOW_INFOS_LISTENER, + REMOVE_WINDOW_INFOS_LISTENER, GET_PRIMARY_PHYSICAL_DISPLAY_ID, // Always append new enum to the end. }; diff --git a/include/input/InputApplication.h b/libs/gui/include/gui/InputApplication.h index 8e4fe796a5..679c2a1754 100644 --- a/include/input/InputApplication.h +++ b/libs/gui/include/gui/InputApplication.h @@ -19,17 +19,17 @@ #include <string> -#include <android/InputApplicationInfo.h> +#include <android/gui/InputApplicationInfo.h> #include <binder/IBinder.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> -#include <input/Input.h> #include <utils/RefBase.h> #include <utils/Timers.h> namespace android { + /* * Handle for an application that can receive input. * @@ -38,13 +38,9 @@ namespace android { */ class InputApplicationHandle { public: - inline const InputApplicationInfo* getInfo() const { - return &mInfo; - } + inline const gui::InputApplicationInfo* getInfo() const { return &mInfo; } - inline std::string getName() const { - return !mInfo.name.empty() ? mInfo.name : "<invalid>"; - } + inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : "<invalid>"; } inline std::chrono::nanoseconds getDispatchingTimeout( std::chrono::nanoseconds defaultValue) const { @@ -52,9 +48,7 @@ public: : defaultValue; } - inline sp<IBinder> getApplicationToken() const { - return mInfo.token; - } + inline sp<IBinder> getApplicationToken() const { return mInfo.token; } bool operator==(const InputApplicationHandle& other) const { return getName() == other.getName() && getApplicationToken() == other.getApplicationToken(); @@ -77,7 +71,7 @@ protected: InputApplicationHandle() = default; virtual ~InputApplicationHandle() = default; - InputApplicationInfo mInfo; + gui::InputApplicationInfo mInfo; }; } // namespace android diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 3e57ff611e..03e4aacdbe 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -26,14 +26,13 @@ #include <gui/ITransactionCompletedListener.h> #include <math/mat4.h> -#ifndef NO_INPUT -#include <android/FocusRequest.h> -#include <input/InputWindow.h> -#endif +#include <android/gui/DropInputMode.h> +#include <android/gui/FocusRequest.h> #include <gui/ISurfaceComposer.h> #include <gui/LayerMetadata.h> #include <gui/SurfaceControl.h> +#include <gui/WindowInfo.h> #include <math/vec3.h> #include <ui/BlurRegion.h> #include <ui/GraphicTypes.h> @@ -119,6 +118,7 @@ struct layer_state_t { eAutoRefreshChanged = 0x1000'00000000, eStretchChanged = 0x2000'00000000, eTrustedOverlayChanged = 0x4000'00000000, + eDropInputModeChanged = 0x8000'00000000, }; layer_state_t(); @@ -178,9 +178,7 @@ struct layer_state_t { mat4 colorTransform; std::vector<BlurRegion> blurRegions; -#ifndef NO_INPUT - sp<InputWindowHandle> inputHandle = new InputWindowHandle(); -#endif + sp<gui::WindowInfoHandle> windowInfoHandle = new gui::WindowInfoHandle(); client_cache_t cachedBuffer; @@ -246,6 +244,14 @@ struct layer_state_t { // is used to remove the old callback from the client process map if it is // overwritten by another setBuffer call. ReleaseCallbackId releaseCallbackId; + + // Stores which endpoint the release information should be sent to. We don't want to send the + // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer + // was called with. + sp<IBinder> releaseBufferEndpoint; + + // Force inputflinger to drop all input events for the layer and its children. + gui::DropInputMode dropInputMode; }; struct ComposerState { @@ -259,7 +265,8 @@ struct DisplayState { eSurfaceChanged = 0x01, eLayerStackChanged = 0x02, eDisplayProjectionChanged = 0x04, - eDisplaySizeChanged = 0x08 + eDisplaySizeChanged = 0x08, + eFlagsChanged = 0x10 }; DisplayState(); @@ -269,6 +276,7 @@ struct DisplayState { sp<IBinder> token; sp<IGraphicBufferProducer> surface; uint32_t layerStack; + uint32_t flags; // These states define how layers are projected onto the physical display. // @@ -292,9 +300,7 @@ struct DisplayState { }; struct InputWindowCommands { -#ifndef NO_INPUT - std::vector<FocusRequest> focusRequests; -#endif + std::vector<gui::FocusRequest> focusRequests; bool syncInputWindows{false}; // Merges the passed in commands and returns true if there were any changes. diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 4164ca36ba..0d1d1a37bd 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -42,6 +42,7 @@ #include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> #include <gui/SurfaceControl.h> +#include <gui/WindowInfosListenerReporter.h> #include <math/vec3.h> namespace android { @@ -56,14 +57,15 @@ class Region; struct SurfaceControlStats { SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime, const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence, - uint32_t hint, FrameEventHistoryStats eventStats) + uint32_t hint, FrameEventHistoryStats eventStats, uint32_t currentMaxAcquiredBufferCount) : surfaceControl(sc), latchTime(latchTime), acquireTime(acquireTime), presentFence(presentFence), previousReleaseFence(prevReleaseFence), transformHint(hint), - frameEventStats(eventStats) {} + frameEventStats(eventStats), + currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {} sp<SurfaceControl> surfaceControl; nsecs_t latchTime = -1; @@ -72,6 +74,7 @@ struct SurfaceControlStats { sp<Fence> previousReleaseFence; uint32_t transformHint = 0; FrameEventHistoryStats frameEventStats; + uint32_t currentMaxAcquiredBufferCount = 0; }; using TransactionCompletedCallbackTakesContext = @@ -503,11 +506,9 @@ public: // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour. Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber); -#ifndef NO_INPUT - Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); - Transaction& setFocusedWindow(const FocusRequest& request); + Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info); + Transaction& setFocusedWindow(const gui::FocusRequest& request); Transaction& syncInputWindows(); -#endif // Set a color transform matrix on the given layer on the built-in display. Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix, @@ -564,12 +565,15 @@ public: Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop); Transaction& setDestinationFrame(const sp<SurfaceControl>& sc, const Rect& destinationFrame); + Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode); status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack); + void setDisplayFlags(const sp<IBinder>& token, uint32_t flags); + /* setDisplayProjection() defines the projection of layer stacks * to a given display. * @@ -623,6 +627,9 @@ public: static status_t removeTunnelModeEnabledListener( const sp<gui::ITunnelModeEnabledListener>& listener); + status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener); + status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener); + private: virtual void onFirstRef(); @@ -652,8 +659,10 @@ public: }; class TransactionCompletedListener : public BnTransactionCompletedListener { +public: TransactionCompletedListener(); +protected: int64_t getNextIdLocked() REQUIRES(mMutex); std::mutex mMutex; @@ -683,13 +692,13 @@ class TransactionCompletedListener : public BnTransactionCompletedListener { std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks GUARDED_BY(mMutex); - std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex); + std::multimap<int32_t, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex); std::unordered_map<ReleaseCallbackId, ReleaseBufferCallback, ReleaseBufferCallbackIdHash> mReleaseBufferCallbacks GUARDED_BY(mMutex); // This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for // std::recursive_mutex - std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> mSurfaceStatsListeners; + std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners; public: static sp<TransactionCompletedListener> getInstance(); @@ -731,8 +740,12 @@ public: void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override; + // For Testing Only + static void setInstance(const sp<TransactionCompletedListener>&); + private: ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&); + static sp<TransactionCompletedListener> sInstance; }; } // namespace android diff --git a/include/input/InputWindow.h b/libs/gui/include/gui/WindowInfo.h index 121be6d963..4727740ab5 100644 --- a/include/input/InputWindow.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -14,15 +14,13 @@ * limitations under the License. */ -#ifndef _UI_INPUT_WINDOW_H -#define _UI_INPUT_WINDOW_H +#pragma once -#include <android/os/TouchOcclusionMode.h> +#include <android/gui/TouchOcclusionMode.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> -#include <input/Flags.h> -#include <input/Input.h> -#include <input/InputTransport.h> +#include <ftl/Flags.h> +#include <gui/constants.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Transform.h> @@ -31,15 +29,13 @@ #include "InputApplication.h" -using android::os::TouchOcclusionMode; - -namespace android { +namespace android::gui { /* * Describes the properties of a window that can receive input. */ -struct InputWindowInfo : public Parcelable { - InputWindowInfo() = default; +struct WindowInfo : public Parcelable { + WindowInfo() = default; // Window flags from WindowManager.LayoutParams enum class Flag : uint32_t { @@ -125,9 +121,11 @@ struct InputWindowInfo : public Parcelable { }; enum class Feature { - DISABLE_TOUCH_PAD_GESTURES = 0x00000001, - NO_INPUT_CHANNEL = 0x00000002, - DISABLE_USER_ACTIVITY = 0x00000004, + DISABLE_TOUCH_PAD_GESTURES = 1u << 0, + NO_INPUT_CHANNEL = 1u << 1, + DISABLE_USER_ACTIVITY = 1u << 2, + DROP_INPUT = 1u << 3, + DROP_INPUT_IF_OBSCURED = 1u << 4, }; /* These values are filled in by the WM and passed through SurfaceFlinger @@ -136,6 +134,10 @@ struct InputWindowInfo : public Parcelable { // This value should NOT be used to uniquely identify the window. There may be different // input windows that have the same token. sp<IBinder> token; + + // The token that identifies which client window this WindowInfo was created for. + sp<IBinder> windowToken; + // This uniquely identifies the input window. int32_t id = -1; std::string name; @@ -168,9 +170,12 @@ struct InputWindowInfo : public Parcelable { // Transform applied to individual windows. ui::Transform transform; + // Display orientation as ui::Transform::RotationFlags. Used for compatibility raw coordinates. + uint32_t displayOrientation = ui::Transform::ROT_0; + // Display size in its natural rotation. Used to rotate raw coordinates for compatibility. - int32_t displayWidth = AMOTION_EVENT_INVALID_DISPLAY_SIZE; - int32_t displayHeight = AMOTION_EVENT_INVALID_DISPLAY_SIZE; + int32_t displayWidth = 0; + int32_t displayHeight = 0; /* * This is filled in by the WM relative to the frame and then translated @@ -206,9 +211,9 @@ struct InputWindowInfo : public Parcelable { bool supportsSplitTouch() const; - bool overlaps(const InputWindowInfo* other) const; + bool overlaps(const WindowInfo* other) const; - bool operator==(const InputWindowInfo& inputChannel) const; + bool operator==(const WindowInfo& inputChannel) const; status_t writeToParcel(android::Parcel* parcel) const override; @@ -221,13 +226,13 @@ struct InputWindowInfo : public Parcelable { * Used by the native input dispatcher to indirectly refer to the window manager objects * that describe a window. */ -class InputWindowHandle : public RefBase { +class WindowInfoHandle : public RefBase { public: - explicit InputWindowHandle(); - InputWindowHandle(const InputWindowHandle& other); - InputWindowHandle(const InputWindowInfo& other); + explicit WindowInfoHandle(); + WindowInfoHandle(const WindowInfoHandle& other); + WindowInfoHandle(const WindowInfo& other); - inline const InputWindowInfo* getInfo() const { return &mInfo; } + inline const WindowInfo* getInfo() const { return &mInfo; } sp<IBinder> getToken() const; @@ -243,21 +248,9 @@ public: } /** - * Requests that the state of this object be updated to reflect - * the most current available information about the application. - * As this class is created as RefBase object, no pure virtual function is allowed. - * - * This method should only be called from within the input dispatcher's - * critical section. - * - * Returns true on success, or false if the handle is no longer valid. - */ - virtual bool updateInfo() { return false; } - - /** * Updates from another input window handle. */ - void updateFrom(const sp<InputWindowHandle> handle); + void updateFrom(const sp<WindowInfoHandle> handle); /** * Releases the channel used by the associated information when it is @@ -270,10 +263,8 @@ public: status_t writeToParcel(android::Parcel* parcel) const; protected: - virtual ~InputWindowHandle(); + virtual ~WindowInfoHandle(); - InputWindowInfo mInfo; + WindowInfo mInfo; }; -} // namespace android - -#endif // _UI_INPUT_WINDOW_H +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h new file mode 100644 index 0000000000..8a70b9bb57 --- /dev/null +++ b/libs/gui/include/gui/WindowInfosListener.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gui/WindowInfo.h> +#include <utils/RefBase.h> + +namespace android::gui { + +class WindowInfosListener : public virtual RefBase { +public: + virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0; +}; +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h new file mode 100644 index 0000000000..7cb96e0a30 --- /dev/null +++ b/libs/gui/include/gui/WindowInfosListenerReporter.h @@ -0,0 +1,48 @@ +/* + * 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 <android/gui/BnWindowInfosListener.h> +#include <android/gui/IWindowInfosReportedListener.h> +#include <binder/IBinder.h> +#include <gui/ISurfaceComposer.h> +#include <gui/WindowInfosListener.h> +#include <utils/Mutex.h> +#include <unordered_set> + +namespace android { +class ISurfaceComposer; + +class WindowInfosListenerReporter : public gui::BnWindowInfosListener { +public: + static sp<WindowInfosListenerReporter> getInstance(); + binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, + const sp<gui::IWindowInfosReportedListener>&) override; + + status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener, + const sp<ISurfaceComposer>&); + status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener, + const sp<ISurfaceComposer>&); + void reconnect(const sp<ISurfaceComposer>&); + +private: + std::mutex mListenersMutex; + std::unordered_set<sp<gui::WindowInfosListener>, + ISurfaceComposer::SpHash<gui::WindowInfosListener>> + mWindowInfosListeners GUARDED_BY(mListenersMutex); +}; +} // namespace android
\ No newline at end of file diff --git a/libs/gui/include/gui/constants.h b/libs/gui/include/gui/constants.h new file mode 100644 index 0000000000..8eab3783e9 --- /dev/null +++ b/libs/gui/include/gui/constants.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stdint.h> + +namespace android { + +/** + * Invalid value for display size. Used when display size isn't available. + */ +constexpr int32_t INVALID_DISPLAY_SIZE = 0; + +enum { + /* Used when an event is not associated with any display. + * Typically used for non-pointer events. */ + ADISPLAY_ID_NONE = -1, + + /* The default display id. */ + ADISPLAY_ID_DEFAULT = 0, +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h index 459b35c544..d2e04268dc 100644 --- a/services/surfaceflinger/tests/utils/CallbackUtils.h +++ b/libs/gui/include/gui/test/CallbackUtils.h @@ -20,8 +20,12 @@ #include <gui/SurfaceControl.h> #include <ui/Fence.h> #include <utils/Timers.h> +#include <chrono> #include <thread> +using ::std::literals::chrono_literals::operator""ms; +using ::std::literals::chrono_literals::operator""s; + namespace android { namespace { @@ -130,10 +134,8 @@ private: void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, nsecs_t latchTime) const { - const auto& - [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence, - transformHint, - frameEvents] = surfaceControlStats; + const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence, + transformHint, frameEvents, ignore] = surfaceControlStats; ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) << "bad acquire time"; @@ -195,7 +197,7 @@ public: std::this_thread::sleep_for(500ms); std::lock_guard lock(mMutex); - EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; + EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received"; mCallbackDataQueue = {}; } @@ -205,5 +207,5 @@ public: std::condition_variable mConditionVariable; std::queue<CallbackData> mCallbackDataQueue; }; -} +} // namespace } // namespace android diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index c801c6243a..3d26c3d858 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -43,6 +43,7 @@ cc_test { "SurfaceTextureMultiContextGL_test.cpp", "Surface_test.cpp", "TextureRenderer.cpp", + "WindowInfo_test.cpp", ], shared_libs: [ diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 9082d275a2..fc7548511d 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -27,6 +27,7 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> +#include <gui/test/CallbackUtils.h> #include <private/gui/ComposerService.h> #include <ui/DisplayMode.h> #include <ui/GraphicBuffer.h> @@ -42,6 +43,29 @@ namespace android { using Transaction = SurfaceComposerClient::Transaction; using android::hardware::graphics::common::V1_2::BufferUsage; +class CountProducerListener : public BnProducerListener { +public: + void onBufferReleased() override { + std::scoped_lock<std::mutex> lock(mMutex); + mNumReleased++; + mReleaseCallback.notify_one(); + } + + void waitOnNumberReleased(int32_t expectedNumReleased) { + std::unique_lock<std::mutex> lock(mMutex); + while (mNumReleased < expectedNumReleased) { + ASSERT_NE(mReleaseCallback.wait_for(lock, std::chrono::seconds(3)), + std::cv_status::timeout) + << "did not receive release"; + } + } + +private: + std::mutex mMutex; + std::condition_variable mReleaseCallback; + int32_t mNumReleased GUARDED_BY(mMutex) = 0; +}; + class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) { @@ -152,18 +176,19 @@ protected: mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB; } - void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) { + void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer, + int32_t maxBufferCount = 2) { producer = adapter.getIGraphicBufferProducer(); - setUpProducer(producer); + setUpProducer(producer, maxBufferCount); } - void setUpProducer(sp<IGraphicBufferProducer>& igbProducer) { + void setUpProducer(sp<IGraphicBufferProducer>& igbProducer, int32_t maxBufferCount) { ASSERT_NE(nullptr, igbProducer.get()); - ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); + ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(maxBufferCount)); IGraphicBufferProducer::QueueBufferOutput qbOutput; + mProducerListener = new CountProducerListener(); ASSERT_EQ(NO_ERROR, - igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, - &qbOutput)); + igbProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); } @@ -287,6 +312,7 @@ protected: DisplayCaptureArgs mCaptureArgs; ScreenCaptureResults mCaptureResults; + sp<CountProducerListener> mProducerListener; }; TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { @@ -672,6 +698,317 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) { /*border*/ 0, /*outsideRegion*/ true)); } +// b/196339769 verify we can can update the requested size while the in FREEZE scaling mode and +// scale the buffer properly when the mode changes to SCALE_TO_WINDOW +TEST_F(BLASTBufferQueueTest, ScalingModeChanges) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight / 4); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight / 4, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); + + uint32_t* bufData; + buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN), + reinterpret_cast<void**>(&bufData)); + fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b); + buf->unlock(); + + IGraphicBufferProducer::QueueBufferOutput qbOutput; + IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */, + HAL_DATASPACE_UNKNOWN, {}, + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + igbProducer->queueBuffer(slot, input, &qbOutput); + adapter.waitForCallbacks(); + } + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, + {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 4})); + + // update the size to half the display and dequeue a buffer quarter of the display. + adapter.update(mSurfaceControl, mDisplayWidth, mDisplayHeight / 2); + + { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight / 8, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); + + uint32_t* bufData; + buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN), + reinterpret_cast<void**>(&bufData)); + g = 255; + fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b); + buf->unlock(); + + IGraphicBufferProducer::QueueBufferOutput qbOutput; + IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */, + HAL_DATASPACE_UNKNOWN, {}, + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, + 0, Fence::NO_FENCE); + igbProducer->queueBuffer(slot, input, &qbOutput); + adapter.waitForCallbacks(); + } + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + // verify we still scale the buffer to the new size (half the screen height) + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, + {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2})); +} + +TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction next; + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + // queue non sync buffer, so this one should get blocked + // Add a present delay to allow the first screenshot to get taken. + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + queueBuffer(igbProducer, r, g, b, presentTimeDelay); + + CallbackHelper transactionCallback; + next.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + + mProducerListener->waitOnNumberReleased(1); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction mainTransaction; + + Transaction next; + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + + mainTransaction.merge(std::move(next)); + // Expect 1 buffer to be released even before sending to SurfaceFlinger + mProducerListener->waitOnNumberReleased(1); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction mainTransaction; + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue another buffer without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 1 buffer to be released because the non sync transaction should merge + // with the sync + mProducerListener->waitOnNumberReleased(1); + + mainTransaction.merge(std::move(next)); + // Expect 2 buffers to be released due to merging the two syncs. + mProducerListener->waitOnNumberReleased(2); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer, 3); + + Transaction mainTransaction; + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue a few buffers without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 3 buffers to be released because the non sync transactions should merge + // with the sync + mProducerListener->waitOnNumberReleased(3); + + mainTransaction.merge(std::move(next)); + // Expect 4 buffers to be released due to merging the two syncs. + mProducerListener->waitOnNumberReleased(4); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +// Tests BBQ with a sync transaction when the buffers acquired reaches max and the only way to +// continue processing is for a release callback from SurfaceFlinger. +// This is done by sending a buffer to SF so it can release the previous one and allow BBQ to +// continue acquiring buffers. +TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer, 4); + + Transaction mainTransaction; + + // Send a buffer to SF + queueBuffer(igbProducer, 0, 255, 0, 0); + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue a few buffers without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + + // apply the first synced buffer to ensure we have to wait on SF + mainTransaction.apply(); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 2 buffers to be released because the non sync transactions should merge + // with the sync + mProducerListener->waitOnNumberReleased(3); + + mainTransaction.merge(std::move(next)); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + class TestProducerListener : public BnProducerListener { public: sp<IGraphicBufferProducer> mIgbp; diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 49c44a78d1..5daef0df28 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -24,6 +24,7 @@ #include <memory> +#include <android/keycodes.h> #include <android/native_window.h> #include <binder/Binder.h> @@ -37,9 +38,9 @@ #include <gui/SurfaceControl.h> #include <android/os/IInputFlinger.h> +#include <gui/WindowInfo.h> #include <input/Input.h> #include <input/InputTransport.h> -#include <input/InputWindow.h> #include <ui/DisplayMode.h> #include <ui/Rect.h> @@ -49,6 +50,11 @@ using android::os::IInputFlinger; using android::hardware::graphics::common::V1_1::BufferUsage; +using android::gui::FocusRequest; +using android::gui::InputApplicationInfo; +using android::gui::TouchOcclusionMode; +using android::gui::WindowInfo; + namespace android::test { using Transaction = SurfaceComposerClient::Transaction; @@ -115,8 +121,8 @@ public: return std::make_unique<InputSurface>(surfaceControl, width, height); } - InputEvent* consumeEvent() { - waitForEventAvailable(); + InputEvent *consumeEvent(int timeoutMs = 3000) { + waitForEventAvailable(timeoutMs); InputEvent *ev; uint32_t seqId; @@ -173,6 +179,24 @@ public: EXPECT_EQ(flags, mev->getFlags() & flags); } + void expectKey(uint32_t keycode) { + InputEvent *ev = consumeEvent(); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType()); + KeyEvent *keyEvent = static_cast<KeyEvent *>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, keyEvent->getAction()); + EXPECT_EQ(keycode, keyEvent->getKeyCode()); + EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS); + + ev = consumeEvent(); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType()); + keyEvent = static_cast<KeyEvent *>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_UP, keyEvent->getAction()); + EXPECT_EQ(keycode, keyEvent->getKeyCode()); + EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS); + } + virtual ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); } @@ -210,19 +234,19 @@ public: } private: - void waitForEventAvailable() { + void waitForEventAvailable(int timeoutMs) { struct pollfd fd; fd.fd = mClientChannel->getFd(); fd.events = POLLIN; - poll(&fd, 1, 3000); + poll(&fd, 1, timeoutMs); } void populateInputInfo(int width, int height) { mInputInfo.token = mClientChannel->getConnectionToken(); mInputInfo.name = "Test info"; - mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL; - mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION; + mInputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL; + mInputInfo.type = WindowInfo::Type::BASE_APPLICATION; mInputInfo.dispatchingTimeout = 5s; mInputInfo.globalScaleFactor = 1.0; mInputInfo.focusable = true; @@ -249,7 +273,7 @@ public: std::shared_ptr<InputChannel> mClientChannel; sp<IInputFlinger> mInputFlinger; - InputWindowInfo mInputInfo; + WindowInfo mInputInfo; PreallocatedInputEventFactory mInputEventFactory; InputConsumer* mInputConsumer; @@ -358,6 +382,14 @@ void injectTap(int x, int y) { } } +void injectKey(uint32_t keycode) { + char *buf1; + asprintf(&buf1, "%d", keycode); + if (fork() == 0) { + execlp("input", "input", "keyevent", buf1, NULL); + } +} + TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); @@ -609,6 +641,9 @@ TEST_F(InputSurfacesTest, can_be_focused) { surface->requestFocus(); surface->assertFocusChange(true); + + injectKey(AKEYCODE_V); + surface->expectKey(AKEYCODE_V); } TEST_F(InputSurfacesTest, rotate_surface) { @@ -686,7 +721,7 @@ TEST_F(InputSurfacesTest, touch_flag_obscured) { // Add non touchable window to fully cover touchable window. Window behind gets touch, but // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; nonTouchableSurface->mInputInfo.ownerUid = 22222; // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by // the default obscured/untrusted touch filter introduced in S. @@ -706,8 +741,8 @@ TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) { // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; - parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; + parentSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; nonTouchableSurface->mInputInfo.ownerUid = 22222; parentSurface->mInputInfo.ownerUid = 22222; nonTouchableSurface->showAt(0, 0); @@ -730,8 +765,8 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) { // the touchable window. Window behind gets touch with no obscured flags. std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; - parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; + parentSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; nonTouchableSurface->mInputInfo.ownerUid = 22222; parentSurface->mInputInfo.ownerUid = 22222; nonTouchableSurface->showAt(0, 0); @@ -751,7 +786,7 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) { std::unique_ptr<InputSurface> bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + bufferSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; bufferSurface->mInputInfo.ownerUid = 22222; surface->showAt(10, 10); @@ -766,7 +801,7 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) { std::unique_ptr<BlastInputSurface> bufferSurface = BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + bufferSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; bufferSurface->mInputInfo.ownerUid = 22222; surface->showAt(10, 10); @@ -776,4 +811,153 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) { surface->expectTap(1, 1); } +TEST_F(InputSurfacesTest, strict_unobscured_input_unobscured_window) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction( + [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); + surface->showAt(100, 100); + + injectTap(101, 101); + + EXPECT_NE(surface->consumeEvent(), nullptr); + EXPECT_NE(surface->consumeEvent(), nullptr); + + surface->requestFocus(); + surface->assertFocusChange(true); + injectKey(AKEYCODE_V); + surface->expectKey(AKEYCODE_V); +} + +TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction([&](auto &t, auto &sc) { + t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); + t.setMatrix(sc, 2.0, 0, 0, 2.0); + }); + surface->showAt(100, 100); + + injectTap(101, 101); + + EXPECT_NE(surface->consumeEvent(), nullptr); + EXPECT_NE(surface->consumeEvent(), nullptr); + + surface->requestFocus(); + surface->assertFocusChange(true); + injectKey(AKEYCODE_V); + surface->expectKey(AKEYCODE_V); +} + +TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->mInputInfo.ownerUid = 11111; + surface->doTransaction( + [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); + surface->showAt(100, 100); + std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100); + obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; + obscuringSurface->mInputInfo.ownerUid = 22222; + obscuringSurface->showAt(100, 100); + injectTap(101, 101); + EXPECT_EQ(surface->consumeEvent(100), nullptr); + + surface->requestFocus(); + surface->assertFocusChange(true); + injectKey(AKEYCODE_V); + EXPECT_EQ(surface->consumeEvent(100), nullptr); +} + +TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->mInputInfo.ownerUid = 11111; + surface->doTransaction( + [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); + surface->showAt(100, 100); + std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100); + obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE; + obscuringSurface->mInputInfo.ownerUid = 22222; + obscuringSurface->showAt(190, 190); + + injectTap(101, 101); + + EXPECT_EQ(surface->consumeEvent(100), nullptr); + + surface->requestFocus(); + surface->assertFocusChange(true); + injectKey(AKEYCODE_V); + EXPECT_EQ(surface->consumeEvent(100), nullptr); +} + +TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { + std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300); + parentSurface->showAt(0, 0, Rect(0, 0, 300, 300)); + + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + surface->doTransaction([&](auto &t, auto &sc) { + t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); + t.reparent(sc, parentSurface->mSurfaceControl); + t.setAlpha(parentSurface->mSurfaceControl, 0.9f); + }); + + injectTap(101, 101); + + EXPECT_EQ(surface->consumeEvent(100), nullptr); + + surface->requestFocus(); + surface->assertFocusChange(true); + injectKey(AKEYCODE_V); + EXPECT_EQ(surface->consumeEvent(100), nullptr); +} + +TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { + std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300); + parentSurface->showAt(0, 0, Rect(0, 0, 300, 300)); + + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction([&](auto &t, auto &sc) { + t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); + t.reparent(sc, parentSurface->mSurfaceControl); + t.setCrop(parentSurface->mSurfaceControl, Rect(10, 10, 100, 100)); + }); + surface->showAt(100, 100); + + injectTap(111, 111); + + EXPECT_EQ(surface->consumeEvent(100), nullptr); + + surface->requestFocus(); + surface->assertFocusChange(true); + injectKey(AKEYCODE_V); + EXPECT_EQ(surface->consumeEvent(100), nullptr); +} + +TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + + std::unique_ptr<BlastInputSurface> bufferSurface = + BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); + + surface->showAt(100, 100); + bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200)); + bufferSurface->showAt(100, 100, Rect::EMPTY_RECT); + + injectTap(101, 101); + surface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, drop_input_policy) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction( + [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::ALL); }); + surface->showAt(100, 100); + + injectTap(101, 101); + + EXPECT_EQ(surface->consumeEvent(100), nullptr); + + surface->requestFocus(); + surface->assertFocusChange(true); + injectKey(AKEYCODE_V); + EXPECT_EQ(surface->consumeEvent(100), nullptr); +} } // namespace android::test diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 630dd17daa..c745505038 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -905,6 +905,16 @@ public: status_t getMaxAcquiredBufferCount(int* /*buffers*/) const override { return NO_ERROR; } + status_t addWindowInfosListener( + const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override { + return NO_ERROR; + } + + status_t removeWindowInfosListener( + const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override { + return NO_ERROR; + } + protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index 493f2f4495..a4f436cdba 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -19,16 +19,20 @@ #include <binder/Binder.h> #include <binder/Parcel.h> -#include <input/InputWindow.h> -#include <input/InputTransport.h> +#include <gui/WindowInfo.h> using std::chrono_literals::operator""s; namespace android { + +using gui::InputApplicationInfo; +using gui::TouchOcclusionMode; +using gui::WindowInfo; + namespace test { -TEST(InputWindowInfo, ParcellingWithoutToken) { - InputWindowInfo i, i2; +TEST(WindowInfo, ParcellingWithoutToken) { + WindowInfo i, i2; i.token = nullptr; Parcel p; @@ -38,14 +42,15 @@ TEST(InputWindowInfo, ParcellingWithoutToken) { ASSERT_TRUE(i2.token == nullptr); } -TEST(InputWindowInfo, Parcelling) { +TEST(WindowInfo, Parcelling) { sp<IBinder> touchableRegionCropHandle = new BBinder(); - InputWindowInfo i; + WindowInfo i; i.token = new BBinder(); + i.windowToken = new BBinder(); i.id = 1; i.name = "Foobar"; - i.flags = InputWindowInfo::Flag::SLIPPERY; - i.type = InputWindowInfo::Type::INPUT_METHOD; + i.flags = WindowInfo::Flag::SLIPPERY; + i.type = WindowInfo::Type::INPUT_METHOD; i.dispatchingTimeout = 12s; i.frameLeft = 93; i.frameTop = 34; @@ -55,6 +60,7 @@ TEST(InputWindowInfo, Parcelling) { i.globalScaleFactor = 0.3; i.alpha = 0.7; i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1}); + i.displayOrientation = ui::Transform::ROT_0; i.displayWidth = 1000; i.displayHeight = 2000; i.visible = false; @@ -65,7 +71,7 @@ TEST(InputWindowInfo, Parcelling) { i.ownerPid = 19; i.ownerUid = 24; i.packageName = "com.example.package"; - i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY; + i.inputFeatures = WindowInfo::Feature::DISABLE_USER_ACTIVITY; i.displayId = 34; i.portalToDisplayId = 2; i.replaceTouchableRegionWithCrop = true; @@ -77,9 +83,10 @@ TEST(InputWindowInfo, Parcelling) { Parcel p; i.writeToParcel(&p); p.setDataPosition(0); - InputWindowInfo i2; + WindowInfo i2; i2.readFromParcel(&p); ASSERT_EQ(i.token, i2.token); + ASSERT_EQ(i.windowToken, i2.windowToken); ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); ASSERT_EQ(i.flags, i2.flags); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index a63ec8fb2a..71a129141d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -30,7 +30,6 @@ filegroup { "android/os/IInputConstants.aidl", "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", - "android/os/TouchOcclusionMode.aidl", ], } @@ -79,16 +78,11 @@ cc_library { android: { srcs: [ "InputTransport.cpp", - "InputWindow.cpp", - "android/FocusRequest.aidl", - "android/InputApplicationInfo.aidl", "android/os/BlockUntrustedTouchesMode.aidl", "android/os/IInputConstants.aidl", "android/os/IInputFlinger.aidl", "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", - "android/os/ISetInputWindowsListener.aidl", - "android/os/TouchOcclusionMode.aidl", ], export_shared_lib_headers: ["libbinder"], @@ -99,6 +93,14 @@ cc_library { "libui", ], + static_libs: [ + "libgui_window_info_static", + ], + + export_static_lib_headers: [ + "libgui_window_info_static", + ], + sanitize: { misc_undefined: ["integer"], }, @@ -111,29 +113,32 @@ cc_library { "frameworks/native/libs/arect/include", ], }, - linux_glibc: { + host_linux: { srcs: [ "InputTransport.cpp", - "InputWindow.cpp", - "android/FocusRequest.aidl", - "android/InputApplicationInfo.aidl", "android/os/IInputConstants.aidl", "android/os/IInputFlinger.aidl", - "android/os/ISetInputWindowsListener.aidl", - "android/os/TouchOcclusionMode.aidl", ], static_libs: [ "libhostgraphics", + "libgui_window_info_static", ], shared_libs: [ "libbinder", ], + + export_static_lib_headers: [ + "libgui_window_info_static", + ], }, }, aidl: { local_include_dirs: ["."], export_aidl_headers: true, + include_dirs: [ + "frameworks/native/libs/gui", + ], }, } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index d954d23507..5f440b77e2 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -25,6 +25,7 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <gui/constants.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> @@ -66,23 +67,15 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { transformedPoint.y -= origin.y; // Derive the transformed vector's clockwise angle from vertical. - float result = atan2f(transformedPoint.x, -transformedPoint.y); - if (result < -M_PI_2) { - result += M_PI; - } else if (result > M_PI_2) { - result -= M_PI; - } - return result; + // The return value of atan2f is in range [-pi, pi] which conforms to the orientation API. + return atan2f(transformedPoint.x, -transformedPoint.y); } -// Rotates the given point to the transform's orientation. If the display width and height are +// Rotates the given point to the specified orientation. If the display width and height are // provided, the point is rotated in the screen space. Otherwise, the point is rotated about the // origin. This helper is used to avoid the extra overhead of creating new Transforms. -vec2 rotatePoint(const ui::Transform& transform, float x, float y, int32_t displayWidth = 0, +vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0, int32_t displayHeight = 0) { - // 0x7 encapsulates all 3 rotations (see ui::Transform::RotationFlags) - static const int ALL_ROTATIONS_MASK = 0x7; - const uint32_t orientation = (transform.getOrientation() & ALL_ROTATIONS_MASK); if (orientation == ui::Transform::ROT_0) { return {x, y}; } @@ -101,6 +94,19 @@ vec2 rotatePoint(const ui::Transform& transform, float x, float y, int32_t displ return xy; } +vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) { + const vec2 transformedXy = transform.transform(x, y); + const vec2 transformedOrigin = transform.transform(0, 0); + return transformedXy - transformedOrigin; +} + +bool shouldDisregardWindowTranslation(uint32_t source) { + // Pointer events are the only type of events that refer to absolute coordinates on the display, + // so we should apply the entire window transform. For other types of events, we should make + // sure to not apply the window translation/offset. + return (source & AINPUT_SOURCE_CLASS_POINTER) == 0; +} + } // namespace const char* motionClassificationToString(MotionClassification classification) { @@ -315,6 +321,8 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_X, windowXScale); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } void PointerCoords::scale(float globalScaleFactor) { @@ -383,6 +391,15 @@ void PointerCoords::transform(const ui::Transform& transform) { setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); + if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_X) || + BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_Y)) { + const ui::Transform rotation(transform.getOrientation()); + const vec2 relativeXy = rotation.transform(getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x); + setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y); + } + if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) { const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val)); @@ -410,9 +427,9 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, - int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, - nsecs_t eventTime, size_t pointerCount, - const PointerProperties* pointerProperties, + uint32_t displayOrientation, int32_t displayWidth, + int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(id, deviceId, source, displayId, hmac); mAction = action; @@ -427,6 +444,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 mYPrecision = yPrecision; mRawXCursorPosition = rawXCursorPosition; mRawYCursorPosition = rawYCursorPosition; + mDisplayOrientation = displayOrientation; mDisplayWidth = displayWidth; mDisplayHeight = displayHeight; mDownTime = downTime; @@ -452,6 +470,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYPrecision = other->mYPrecision; mRawXCursorPosition = other->mRawXCursorPosition; mRawYCursorPosition = other->mRawYCursorPosition; + mDisplayOrientation = other->mDisplayOrientation; mDisplayWidth = other->mDisplayWidth; mDisplayHeight = other->mDisplayHeight; mDownTime = other->mDownTime; @@ -521,25 +540,48 @@ float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { // For compatibility, convert raw coordinates into "oriented screen space". Once app // developers are educated about getRaw, we can consider removing this. - const vec2 xy = rotatePoint(mTransform, coords->getX(), coords->getY(), mDisplayWidth, - mDisplayHeight); + const vec2 xy = shouldDisregardWindowTranslation(mSource) + ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY()) + : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth, + mDisplayHeight); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } + if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { + // For compatibility, since we convert raw coordinates into "oriented screen space", we + // need to convert the relative axes into the same orientation for consistency. + const vec2 relativeXy = rotatePoint(mDisplayOrientation, + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; + } + return coords->getAxisValue(axis); } float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, - size_t historicalIndex) const { + size_t historicalIndex) const { const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = mTransform.transform(coords->getXYValue()); + const vec2 xy = shouldDisregardWindowTranslation(mSource) + ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY()) + : mTransform.transform(coords->getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } + if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { + const vec2 relativeXy = + applyTransformWithoutTranslation(mTransform, + coords->getAxisValue( + AMOTION_EVENT_AXIS_RELATIVE_X), + coords->getAxisValue( + AMOTION_EVENT_AXIS_RELATIVE_Y)); + return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; + } + return coords->getAxisValue(axis); } @@ -595,6 +637,13 @@ void MotionEvent::applyTransform(const std::array<float, 9>& matrix) { // Apply the transformation to all samples. std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), [&transform](PointerCoords& c) { c.transform(transform); }); + + if (mRawXCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION && + mRawYCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) { + const vec2 cursor = transform.transform(mRawXCursorPosition, mRawYCursorPosition); + mRawXCursorPosition = cursor.x; + mRawYCursorPosition = cursor.y; + } } #ifdef __linux__ @@ -655,6 +704,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYPrecision = parcel->readFloat(); mRawXCursorPosition = parcel->readFloat(); mRawYCursorPosition = parcel->readFloat(); + mDisplayOrientation = parcel->readUint32(); mDisplayWidth = parcel->readInt32(); mDisplayHeight = parcel->readInt32(); mDownTime = parcel->readInt64(); @@ -716,6 +766,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYPrecision); parcel->writeFloat(mRawXCursorPosition); parcel->writeFloat(mRawYCursorPosition); + parcel->writeUint32(mDisplayOrientation); parcel->writeInt32(mDisplayWidth); parcel->writeInt32(mDisplayHeight); parcel->writeInt64(mDownTime); diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index fcbc16f9c7..1aec477081 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -21,9 +21,9 @@ #include <ctype.h> #include <android-base/stringprintf.h> +#include <ftl/NamedEnum.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> -#include <input/NamedEnum.h> using android::base::StringPrintf; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index dd1c46265c..ea8b9a7ec8 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -33,8 +33,8 @@ static constexpr bool DEBUG_TRANSPORT_ACTIONS = false; #include <log/log.h> #include <utils/Trace.h> +#include <ftl/NamedEnum.h> #include <input/InputTransport.h> -#include <input/NamedEnum.h> using android::base::StringPrintf; @@ -242,9 +242,11 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.xCursorPosition = body.motion.xCursorPosition; // float yCursorPosition msg->body.motion.yCursorPosition = body.motion.yCursorPosition; - // int32_t displayW + // uint32_t displayOrientation + msg->body.motion.displayOrientation = body.motion.displayOrientation; + // int32_t displayWidth msg->body.motion.displayWidth = body.motion.displayWidth; - // int32_t displayH + // int32_t displayHeight msg->body.motion.displayHeight = body.motion.displayHeight; // uint32_t pointerCount msg->body.motion.pointerCount = body.motion.pointerCount; @@ -533,9 +535,10 @@ status_t InputPublisher::publishMotionEvent( std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, int32_t displayWidth, - int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { + float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation, + int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", @@ -593,6 +596,7 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.yPrecision = yPrecision; msg.body.motion.xCursorPosition = xCursorPosition; msg.body.motion.yCursorPosition = yCursorPosition; + msg.body.motion.displayOrientation = displayOrientation; msg.body.motion.displayWidth = displayWidth; msg.body.motion.displayHeight = displayHeight; msg.body.motion.downTime = downTime; @@ -1361,9 +1365,9 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage msg->body.motion.buttonState, msg->body.motion.classification, transform, msg->body.motion.xPrecision, msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.displayWidth, msg->body.motion.displayHeight, - msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, - pointerProperties, pointerCoords); + msg->body.motion.displayOrientation, msg->body.motion.displayWidth, + msg->body.motion.displayHeight, msg->body.motion.downTime, + msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 44f3f34994..2039fa6553 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -28,10 +28,11 @@ #include <input/KeyCharacterMap.h> #include <input/Keyboard.h> -#include <utils/Log.h> +#include <gui/constants.h> #include <utils/Errors.h> -#include <utils/Tokenizer.h> +#include <utils/Log.h> #include <utils/Timers.h> +#include <utils/Tokenizer.h> // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -85,10 +86,13 @@ static String8 toString(const char16_t* chars, size_t numChars) { // --- KeyCharacterMap --- -KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {} +KeyCharacterMap::KeyCharacterMap(const std::string& filename) + : mType(KeyboardType::UNKNOWN), mLoadFileName(filename) {} KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : mType(other.mType), + mLoadFileName(other.mLoadFileName), + mLayoutOverlayApplied(other.mLayoutOverlayApplied), mKeysByScanCode(other.mKeysByScanCode), mKeysByUsageCode(other.mKeysByUsageCode) { for (size_t i = 0; i < other.mKeys.size(); i++) { @@ -97,16 +101,19 @@ KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) } KeyCharacterMap::~KeyCharacterMap() { - for (size_t i = 0; i < mKeys.size(); i++) { - Key* key = mKeys.editValueAt(i); - delete key; - } + clear(); } bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { if (mType != other.mType) { return false; } + if (mLoadFileName != other.mLoadFileName) { + return false; + } + if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) { + return false; + } if (mKeys.size() != other.mKeys.size() || mKeysByScanCode.size() != other.mKeysByScanCode.size() || mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) { @@ -145,6 +152,10 @@ bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { return true; } +bool KeyCharacterMap::operator!=(const KeyCharacterMap& other) const { + return !(*this == other); +} + base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename, Format format) { Tokenizer* tokenizer; @@ -152,12 +163,18 @@ base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std:: if (status) { return Errorf("Error {} opening key character map file {}.", status, filename.c_str()); } + std::shared_ptr<KeyCharacterMap> map = + std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename)); + if (!map.get()) { + ALOGE("Error allocating key character map."); + return Errorf("Error allocating key character map."); + } std::unique_ptr<Tokenizer> t(tokenizer); - auto ret = load(t.get(), format); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + status = map->load(t.get(), format); + if (status == OK) { + return map; } - return ret; + return Errorf("Load KeyCharacterMap failed {}.", status); } base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents( @@ -168,40 +185,67 @@ base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents( ALOGE("Error %d opening key character map.", status); return Errorf("Error {} opening key character map.", status); } + std::shared_ptr<KeyCharacterMap> map = + std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename)); + if (!map.get()) { + ALOGE("Error allocating key character map."); + return Errorf("Error allocating key character map."); + } std::unique_ptr<Tokenizer> t(tokenizer); - auto ret = load(t.get(), format); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + status = map->load(t.get(), format); + if (status == OK) { + return map; } - return ret; + return Errorf("Load KeyCharacterMap failed {}.", status); } -base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer, - Format format) { +status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) { status_t status = OK; - std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap()); - if (!map.get()) { - ALOGE("Error allocating key character map."); - return Errorf("Error allocating key character map."); - } #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map.get(), tokenizer, format); + Parser parser(this, tokenizer, format); status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif - if (status == OK) { - return map; + if (status != OK) { + ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str()); } + return status; +} - return Errorf("Load KeyCharacterMap failed {}.", status); +void KeyCharacterMap::clear() { + mKeysByScanCode.clear(); + mKeysByUsageCode.clear(); + for (size_t i = 0; i < mKeys.size(); i++) { + Key* key = mKeys.editValueAt(i); + delete key; + } + mKeys.clear(); + mLayoutOverlayApplied = false; + mType = KeyboardType::UNKNOWN; +} + +status_t KeyCharacterMap::reloadBaseFromFile() { + clear(); + Tokenizer* tokenizer; + status_t status = Tokenizer::open(String8(mLoadFileName.c_str()), &tokenizer); + if (status) { + ALOGE("Error %s opening key character map file %s.", statusToString(status).c_str(), + mLoadFileName.c_str()); + return status; + } + std::unique_ptr<Tokenizer> t(tokenizer); + return load(t.get(), KeyCharacterMap::Format::BASE); } void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { + if (mLayoutOverlayApplied) { + reloadBaseFromFile(); + } for (size_t i = 0; i < overlay.mKeys.size(); i++) { int32_t keyCode = overlay.mKeys.keyAt(i); Key* key = overlay.mKeys.valueAt(i); @@ -223,7 +267,7 @@ void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i), overlay.mKeysByUsageCode.valueAt(i)); } - mLoadFileName = overlay.mLoadFileName; + mLayoutOverlayApplied = true; } KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const { @@ -635,8 +679,11 @@ std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) ALOGE("%s: Null parcel", __func__); return nullptr; } - std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap()); + std::string loadFileName = parcel->readCString(); + std::shared_ptr<KeyCharacterMap> map = + std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(loadFileName)); map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32()); + map->mLayoutOverlayApplied = parcel->readBool(); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; @@ -686,6 +733,30 @@ std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) return nullptr; } } + size_t numKeysByScanCode = parcel->readInt32(); + if (parcel->errorCheck()) { + return nullptr; + } + for (size_t i = 0; i < numKeysByScanCode; i++) { + int32_t key = parcel->readInt32(); + int32_t value = parcel->readInt32(); + map->mKeysByScanCode.add(key, value); + if (parcel->errorCheck()) { + return nullptr; + } + } + size_t numKeysByUsageCode = parcel->readInt32(); + if (parcel->errorCheck()) { + return nullptr; + } + for (size_t i = 0; i < numKeysByUsageCode; i++) { + int32_t key = parcel->readInt32(); + int32_t value = parcel->readInt32(); + map->mKeysByUsageCode.add(key, value); + if (parcel->errorCheck()) { + return nullptr; + } + } return map; } @@ -694,7 +765,9 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { ALOGE("%s: Null parcel", __func__); return; } + parcel->writeCString(mLoadFileName.c_str()); parcel->writeInt32(static_cast<int32_t>(mType)); + parcel->writeBool(mLayoutOverlayApplied); size_t numKeys = mKeys.size(); parcel->writeInt32(numKeys); @@ -714,6 +787,18 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { } parcel->writeInt32(0); } + size_t numKeysByScanCode = mKeysByScanCode.size(); + parcel->writeInt32(numKeysByScanCode); + for (size_t i = 0; i < numKeysByScanCode; i++) { + parcel->writeInt32(mKeysByScanCode.keyAt(i)); + parcel->writeInt32(mKeysByScanCode.valueAt(i)); + } + size_t numKeysByUsageCode = mKeysByUsageCode.size(); + parcel->writeInt32(numKeysByUsageCode); + for (size_t i = 0; i < numKeysByUsageCode; i++) { + parcel->writeInt32(mKeysByUsageCode.keyAt(i)); + parcel->writeInt32(mKeysByUsageCode.valueAt(i)); + } } #endif // __linux__ diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index fa5a5412e6..c365ab070e 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -19,10 +19,10 @@ #include <stdlib.h> #include <android/keycodes.h> +#include <ftl/NamedEnum.h> #include <input/InputEventLabels.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> -#include <input/NamedEnum.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/Timers.h> diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl index 1771d192af..00ebd4d34f 100644 --- a/libs/input/android/os/IInputFlinger.aidl +++ b/libs/input/android/os/IInputFlinger.aidl @@ -16,20 +16,13 @@ package android.os; -import android.FocusRequest; import android.InputChannel; -import android.InputWindowInfo; -import android.os.ISetInputWindowsListener; +import android.gui.FocusRequest; +import android.gui.WindowInfo; /** @hide */ interface IInputFlinger { - // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the - // ordering when needed. - // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer - // shouldn't be a concern. - oneway void setInputWindows(in InputWindowInfo[] inputHandles, - in @nullable ISetInputWindowsListener setInputWindowsListener); InputChannel createInputChannel(in @utf8InCpp String name); void removeInputChannel(in IBinder connectionToken); /** diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 6ffc6a8926..6ffe8518b6 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -11,26 +11,24 @@ package { cc_test { name: "libinput_tests", srcs: [ - "NamedEnum_test.cpp", - "Flags_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", - "InputWindow_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", "VerifiedInputEvent_test.cpp", ], + static_libs: [ + "libgui_window_info_static", + "libinput", + ], cflags: [ "-Wall", "-Wextra", "-Werror", ], - static_libs: [ - "libinput", - ], shared_libs: [ "libbase", "libbinder", @@ -39,6 +37,7 @@ cc_test { "libui", "libutils", ], + data: ["data/*.kcm"], test_suites: ["device-tests"], } @@ -61,5 +60,5 @@ cc_library_static { "libbinder", "libui", "libbase", - ] + ], } diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index f8f2f4e931..61e88df11d 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -20,6 +20,7 @@ #include <input/InputDevice.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> +#include "android-base/file.h" namespace android { @@ -82,4 +83,53 @@ TEST_F(InputDeviceKeyMapTest, keyCharacterMapParcelingTest) { ASSERT_EQ(*map, *mKeyMap.keyCharacterMap); } +TEST_F(InputDeviceKeyMapTest, keyCharacterMapWithOverlayParcelingTest) { + Parcel parcel; + std::string overlayPath = base::GetExecutableDirectory() + "/data/german.kcm"; + base::Result<std::shared_ptr<KeyCharacterMap>> overlay = + KeyCharacterMap::load(overlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(overlay.ok()) << "Cannot load KeyCharacterMap at " << overlayPath; + mKeyMap.keyCharacterMap->combine(*overlay->get()); + mKeyMap.keyCharacterMap->writeToParcel(&parcel); + parcel.setDataPosition(0); + std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel); + ASSERT_EQ(*map, *mKeyMap.keyCharacterMap); +} + +TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { + std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm"; + std::string englishOverlayPath = base::GetExecutableDirectory() + "/data/english_us.kcm"; + std::string germanOverlayPath = base::GetExecutableDirectory() + "/data/german.kcm"; + base::Result<std::shared_ptr<KeyCharacterMap>> frenchOverlay = + KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath; + base::Result<std::shared_ptr<KeyCharacterMap>> englishOverlay = + KeyCharacterMap::load(englishOverlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(englishOverlay.ok()) << "Cannot load KeyCharacterMap at " << englishOverlayPath; + base::Result<std::shared_ptr<KeyCharacterMap>> germanOverlay = + KeyCharacterMap::load(germanOverlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(germanOverlay.ok()) << "Cannot load KeyCharacterMap at " << germanOverlayPath; + + // Apply the French overlay + mKeyMap.keyCharacterMap->combine(*frenchOverlay->get()); + // Copy the result for later + std::shared_ptr<KeyCharacterMap> frenchOverlaidKeyCharacterMap = + std::make_shared<KeyCharacterMap>(*mKeyMap.keyCharacterMap); + + // Apply the English overlay + mKeyMap.keyCharacterMap->combine(*englishOverlay->get()); + // Verify that the result is different from the French overlay result + ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); + + // Apply the German overlay + mKeyMap.keyCharacterMap->combine(*germanOverlay->get()); + // Verify that the result is different from the French overlay result + ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); + + // Apply the French overlay + mKeyMap.keyCharacterMap->combine(*frenchOverlay->get()); + // Verify that the result is the same like after applying it initially + ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); +} + } // namespace android diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 3b76ddbb7c..b1ef7534e4 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -21,6 +21,7 @@ #include <attestation/HmacKeyManager.h> #include <binder/Parcel.h> #include <gtest/gtest.h> +#include <gui/constants.h> #include <input/Input.h> namespace android { @@ -293,7 +294,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -610,12 +611,12 @@ TEST_F(MotionEventTest, Transform) { } MotionEvent event; ui::Transform identityTransform; - event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; @@ -658,13 +659,17 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); } -MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) { +MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, + const ui::Transform& transform, + uint32_t displayOrientation = ui::Transform::ROT_0) { std::vector<PointerProperties> pointerProperties; pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER}); std::vector<PointerCoords> pointerCoords; pointerCoords.emplace_back().clear(); pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x); pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, dx); + pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy); nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC); MotionEvent event; event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN, @@ -672,7 +677,8 @@ MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) { /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, transform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400, + AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation, + /* displayWidth */ 400, /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(), pointerProperties.data(), pointerCoords.data()); return event; @@ -683,26 +689,56 @@ TEST_F(MotionEventTest, ApplyTransform) { ui::Transform identity; ui::Transform xform(ui::Transform::ROT_90, 800, 400); xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, xform); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); ASSERT_EQ(700, event.getRawX(0)); ASSERT_EQ(60, event.getRawY(0)); ASSERT_NE(event.getRawX(0), event.getX(0)); ASSERT_NE(event.getRawY(0), event.getY(0)); + // Relative values should be rotated but not translated. + ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - MotionEvent changedEvent = createTouchDownEvent(60, 100, identity); + MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity); const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0], xform[0][1], xform[1][1], xform[2][1], xform[0][2], xform[1][2], xform[2][2]}; changedEvent.applyTransform(rowMajor); // transformContent effectively rotates the raw coordinates, so those should now include - // both rotation AND offset + // both rotation AND offset. ASSERT_EQ(720, changedEvent.getRawX(0)); ASSERT_EQ(100, changedEvent.getRawY(0)); + // Relative values should be rotated but not translated. + ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - // The transformed output should be the same then + // The transformed output should be the same then. ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001); ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001); + ASSERT_NEAR(event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), + changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), 0.001); + ASSERT_NEAR(event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), + changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001); +} + +TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { + constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL, + AINPUT_SOURCE_MOUSE_RELATIVE, + AINPUT_SOURCE_JOYSTICK}; + for (uint32_t source : NON_POINTER_SOURCES) { + // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). + ui::Transform xform(ui::Transform::ROT_90, 800, 400); + xform.set(xform.tx() + 20, xform.ty() + 40); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); + event.setSource(source); + + // Since this event comes from a non-pointer source, it should include rotation but not + // translation/offset. + ASSERT_EQ(-100, event.getX(0)); + ASSERT_EQ(60, event.getY(0)); + ASSERT_EQ(event.getRawX(0), event.getX(0)); + ASSERT_EQ(event.getRawY(0), event.getY(0)); + } } TEST_F(MotionEventTest, RawCompatTransform) { @@ -710,11 +746,14 @@ TEST_F(MotionEventTest, RawCompatTransform) { // Make sure raw is raw regardless of transform translation. ui::Transform xform; xform.set(20, 40); - MotionEvent event = createTouchDownEvent(60, 100, xform); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform); ASSERT_EQ(60, event.getRawX(0)); ASSERT_EQ(100, event.getRawY(0)); ASSERT_NE(event.getRawX(0), event.getX(0)); ASSERT_NE(event.getRawY(0), event.getY(0)); + // Relative values should not be modified. + ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); } // Next check that getRaw contains rotation (for compatibility) but otherwise is still @@ -723,29 +762,50 @@ TEST_F(MotionEventTest, RawCompatTransform) { // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform xform(ui::Transform::ROT_90, 800, 400); xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, xform); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); ASSERT_EQ(700, event.getRawX(0)); ASSERT_EQ(60, event.getRawY(0)); ASSERT_NE(event.getRawX(0), event.getX(0)); ASSERT_NE(event.getRawY(0), event.getY(0)); + // Relative values should be rotated but not translated. + ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); } { // Same as above, but check rotate-180. ui::Transform xform(ui::Transform::ROT_180, 400, 800); xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, xform); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180); ASSERT_EQ(340, event.getRawX(0)); ASSERT_EQ(700, event.getRawY(0)); + // Relative values should be rotated but not translated. + ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); } { // Same as above, but check rotate-270. ui::Transform xform(ui::Transform::ROT_270, 800, 400); xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, xform); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270); ASSERT_EQ(100, event.getRawX(0)); ASSERT_EQ(340, event.getRawY(0)); + // Relative values should be rotated but not translated. + ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); + } + + { + // Finally, check that raw isn't effected by transform + ui::Transform xform(ui::Transform::ROT_270, 800, 400); + xform.set(xform.tx() + 20, xform.ty() + 40); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); + ASSERT_EQ(700, event.getRawX(0)); + ASSERT_EQ(60, event.getRawY(0)); + // Relative values should be rotated but not translated. + ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); } } @@ -772,9 +832,9 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, - pointerCount, pointerProperties, pointerCoords); + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, + 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } } @@ -794,10 +854,9 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, - pointerCoords); + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, + pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); ASSERT_EQ(540, event.getRawYCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index a2cfaa1cc0..5d1f2c3bfc 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -23,6 +23,7 @@ #include <attestation/HmacKeyManager.h> #include <cutils/ashmem.h> #include <gtest/gtest.h> +#include <gui/constants.h> #include <input/InputTransport.h> #include <utils/StopWatch.h> #include <utils/Timers.h> @@ -162,6 +163,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr float yPrecision = 0.5; constexpr float xCursorPosition = 1.3; constexpr float yCursorPosition = 50.6; + constexpr uint32_t displayOrientation = ui::Transform::ROT_0; constexpr int32_t displayWidth = 1000; constexpr int32_t displayHeight = 2000; constexpr nsecs_t downTime = 3; @@ -192,9 +194,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, classification, transform, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, displayWidth, - displayHeight, downTime, eventTime, pointerCount, - pointerProperties, pointerCoords); + xCursorPosition, yCursorPosition, displayOrientation, + displayWidth, displayHeight, downTime, eventTime, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -231,6 +233,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); + EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation()); EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x); EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y); EXPECT_EQ(downTime, motionEvent->getDownTime()); @@ -460,8 +463,9 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + AMOTION_EVENT_INVALID_CURSOR_POSITION, + ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -476,8 +480,9 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + AMOTION_EVENT_INVALID_CURSOR_POSITION, + ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -497,8 +502,9 @@ TEST_F(InputPublisherAndConsumerTest, status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + AMOTION_EVENT_INVALID_CURSOR_POSITION, + ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 5861d55156..59fed1fb41 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -74,10 +74,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124); CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128); CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132); - CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 136); - CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 140); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 144); - CHECK_OFFSET(InputMessage::Body::Motion, empty3, 148); + CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136); + CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140); + CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148); CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152); CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index aefc2ec47d..13e2b02ca4 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -23,6 +23,7 @@ #include <android-base/stringprintf.h> #include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> +#include <gui/constants.h> #include <input/VelocityTracker.h> using namespace std::chrono_literals; @@ -183,8 +184,8 @@ static std::vector<MotionEvent> createMotionEventStream( AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index f79098c63c..b29c9a4877 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -16,6 +16,7 @@ #include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> +#include <gui/constants.h> #include <input/Input.h> namespace android { @@ -46,10 +47,9 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - 100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties, - pointerCoords); + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/, + 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); return event; } diff --git a/libs/input/tests/data/english_us.kcm b/libs/input/tests/data/english_us.kcm new file mode 100644 index 0000000000..d0ef027f85 --- /dev/null +++ b/libs/input/tests/data/english_us.kcm @@ -0,0 +1,311 @@ +# 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. + +# +# English (US) keyboard layout. +# Unlike the default (generic) keyboard layout, English (US) does not contain any +# special ALT characters. +# + +type OVERLAY + +### ROW 1 + +key GRAVE { + label: '`' + base: '`' + shift: '~' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '@' +} + +key 3 { + label: '3' + base: '3' + shift: '#' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '^' +} + +key 7 { + label: '7' + base: '7' + shift: '&' +} + +key 8 { + label: '8' + base: '8' + shift: '*' +} + +key 9 { + label: '9' + base: '9' + shift: '(' +} + +key 0 { + label: '0' + base: '0' + shift: ')' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' +} + +### ROW 2 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '[' + base: '[' + shift: '{' +} + +key RIGHT_BRACKET { + label: ']' + base: ']' + shift: '}' +} + +key BACKSLASH { + label: '\\' + base: '\\' + shift: '|' +} + +### ROW 3 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: ':' +} + +key APOSTROPHE { + label: '\'' + base: '\'' + shift: '"' +} + +### ROW 4 + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key COMMA { + label: ',' + base: ',' + shift: '<' +} + +key PERIOD { + label: '.' + base: '.' + shift: '>' +} + +key SLASH { + label: '/' + base: '/' + shift: '?' +}
\ No newline at end of file diff --git a/libs/input/tests/data/french.kcm b/libs/input/tests/data/french.kcm new file mode 100644 index 0000000000..db69ea0430 --- /dev/null +++ b/libs/input/tests/data/french.kcm @@ -0,0 +1,336 @@ +# 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. + +# +# French keyboard layout, AZERTY style. +# + +type OVERLAY + +map key 16 A +map key 17 Z +map key 30 Q +map key 39 M +map key 44 W +map key 50 COMMA +map key 51 SEMICOLON +map key 86 PLUS + +### ROW 1 + +key GRAVE { + label: '\u00b2' + base: '\u00b2' +} + +key 1 { + label: '1' + base: '&' + shift: '1' +} + +key 2 { + label: '2' + base: '\u00e9' + shift: '2' + ralt: '~' +} + +key 3 { + label: '3' + base: '"' + shift: '3' + ralt: '#' +} + +key 4 { + label: '4' + base: '\'' + shift: '4' + ralt: '{' +} + +key 5 { + label: '5' + base: '(' + shift: '5' + ralt: '[' +} + +key 6 { + label: '6' + base: '-' + shift: '6' + ralt: '|' +} + +key 7 { + label: '7' + base: '\u00e8' + shift: '7' + ralt: '`' +} + +key 8 { + label: '8' + base: '_' + shift: '8' + ralt: '\\' +} + +key 9 { + label: '9' + base: '\u00e7' + shift: '9' + ralt: '^' +} + +key 0 { + label: '0' + base: '\u00e0' + shift: '0' + ralt: '@' +} + +key MINUS { + label: ')' + base: ')' + shift: '\u00b0' + ralt: ']' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' + ralt: '}' +} + +### ROW 2 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ralt: '\u20ac' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '\u02c6' + base: '\u0302' + shift: '\u0308' +} + +key RIGHT_BRACKET { + label: '$' + base: '$' + shift: '\u00a3' + ralt: '\u00a4' +} + +### ROW 3 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key APOSTROPHE { + label: '\u00f9' + base: '\u00f9' + shift: '%' +} + +key BACKSLASH { + label: '*' + base: '*' + shift: '\u00b5' +} + +### ROW 4 + +key PLUS { + label: '<' + base: '<' + shift: '>' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key COMMA { + label: ',' + base: ',' + shift: '?' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: '.' +} + +key PERIOD { + label: ':' + base: ':' + shift: '/' +} + +key SLASH { + label: '!' + base: '!' + shift: '\u00a7' +}
\ No newline at end of file diff --git a/libs/input/tests/data/german.kcm b/libs/input/tests/data/german.kcm new file mode 100644 index 0000000000..2fbc5e54e3 --- /dev/null +++ b/libs/input/tests/data/german.kcm @@ -0,0 +1,336 @@ +# 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. + +# +# German keyboard layout, QWERTZ style. +# + +type OVERLAY + +map key 12 SLASH # � ? \ +map key 21 Z +map key 44 Y +map key 53 MINUS # - _ +map key 86 PLUS # < > | + +map key usage 32 A # for testing purposes only +map key usage 67 B # for testing purposes only + +### ROW 1 + +key GRAVE { + label: '^' + base: '^' + shift: '\u00b0' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '"' + ralt: '\u00b2' +} + +key 3 { + label: '3' + base: '3' + shift: '\u00a7' + ralt: '\u00b3' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '&' +} + +key 7 { + label: '7' + base: '7' + shift: '/' + ralt: '{' +} + +key 8 { + label: '8' + base: '8' + shift: '(' + ralt: '[' +} + +key 9 { + label: '9' + base: '9' + shift: ')' + ralt: ']' +} + +key 0 { + label: '0' + base: '0' + shift: '=' + ralt: '}' +} + +key SLASH { + label: '\u00df' + base: '\u00df' + shift: '?' + ralt: '\\' +} + +key EQUALS { + label: '\u00b4' + base: '\u0301' + shift: '\u0300' +} + +### ROW 2 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' + ralt: '@' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ralt: '\u20ac' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '\u00dc' + base: '\u00fc' + shift, capslock: '\u00dc' +} + +key RIGHT_BRACKET { + label: '+' + base: '+' + shift: '*' + ralt: '~' +} + +### ROW 3 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key SEMICOLON { + label: '\u00d6' + base: '\u00f6' + shift, capslock: '\u00d6' +} + +key APOSTROPHE { + label: '\u00c4' + base: '\u00e4' + shift, capslock: '\u00c4' +} + +key BACKSLASH { + label: '#' + base: '#' + shift: '\'' +} + +### ROW 4 + +key PLUS { + label: '<' + base: '<' + shift: '>' + ralt: '|' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' + ralt: '\u00b5' +} + +key COMMA { + label: ',' + base: ',' + shift: ';' +} + +key PERIOD { + label: '.' + base: '.' + shift: ':' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 0edb213089..2dd6c4fcaa 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -20,7 +20,6 @@ #include <android-base/thread_annotations.h> #include <gui/DisplayEventDispatcher.h> #include <gui/ISurfaceComposer.h> -#include <gui/SurfaceComposerClient.h> #include <jni.h> #include <private/android/choreographer.h> #include <utils/Looper.h> diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index ef7602f24e..af55623ac5 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -207,7 +207,11 @@ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, if (result == 0) { outPlanes->planeCount = 3; outPlanes->planes[0].data = yuvData.y; - outPlanes->planes[0].pixelStride = 1; + if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010) { + outPlanes->planes[0].pixelStride = 2; + } else { + outPlanes->planes[0].pixelStride = 1; + } outPlanes->planes[0].rowStride = yuvData.ystride; outPlanes->planes[1].data = yuvData.cb; outPlanes->planes[1].pixelStride = yuvData.chroma_step; diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 4cb1b4259b..14627ce0ce 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -71,7 +71,7 @@ public: void cleanupPostRender() override; int getContextPriority() override; bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; } - void onPrimaryDisplaySizeChanged(ui::Size size) override {} + void onActiveDisplaySizeChanged(ui::Size size) override {} EGLDisplay getEGLDisplay() const { return mEGLDisplay; } // Creates an output image for rendering to diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 46a7d1eea6..f555cdbc0d 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -129,9 +129,9 @@ public: // Attempt to switch RenderEngine into and out of protectedContext mode virtual void useProtectedContext(bool useProtectedContext) = 0; - // Notify RenderEngine of changes to the dimensions of the primary display + // Notify RenderEngine of changes to the dimensions of the active display // so that it can configure its internal caches accordingly. - virtual void onPrimaryDisplaySizeChanged(ui::Size size) = 0; + virtual void onActiveDisplaySizeChanged(ui::Size size) = 0; // Renders layers for a particular display via GPU composition. This method // should be called for every display that needs to be rendered via the GPU. diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index a4aa9ea488..0be3ba6b11 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -54,7 +54,7 @@ public: MOCK_METHOD0(cleanFramebufferCache, void()); MOCK_METHOD0(getContextPriority, int()); MOCK_METHOD0(supportsBackgroundBlur, bool()); - MOCK_METHOD1(onPrimaryDisplaySizeChanged, void(ui::Size)); + MOCK_METHOD1(onActiveDisplaySizeChanged, void(ui::Size)); protected: // mock renderengine still needs to implement these, but callers should never need to call them. diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 94023e6247..e42b5b9e79 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -1413,7 +1413,7 @@ int SkiaGLRenderEngine::getContextPriority() { return value; } -void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) { +void SkiaGLRenderEngine::onActiveDisplaySizeChanged(ui::Size size) { // This cache multiplier was selected based on review of cache sizes relative // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x), // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 238ad8f452..e1162f5574 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -67,7 +67,7 @@ public: void useProtectedContext(bool useProtectedContext) override; bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; } void assertShadersCompiled(int numShaders) override; - void onPrimaryDisplaySizeChanged(ui::Size size) override; + void onActiveDisplaySizeChanged(ui::Size size) override; int reportShadersCompiled() override; protected: diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index b9dabc1d05..8e666d5733 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -361,14 +361,14 @@ bool RenderEngineThreaded::supportsBackgroundBlur() { return mRenderEngine->supportsBackgroundBlur(); } -void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) { +void RenderEngineThreaded::onActiveDisplaySizeChanged(ui::Size size) { // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([size](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged"); - instance.onPrimaryDisplaySizeChanged(size); + ATRACE_NAME("REThreaded::onActiveDisplaySizeChanged"); + instance.onActiveDisplaySizeChanged(size); }); } mCondition.notify_one(); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index f2f5c0f7cd..b197df7e0f 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -65,7 +65,7 @@ public: void cleanFramebufferCache() override; int getContextPriority() override; bool supportsBackgroundBlur() override; - void onPrimaryDisplaySizeChanged(ui::Size size) override; + void onActiveDisplaySizeChanged(ui::Size size) override; protected: void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override; diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 9dc9beb8e7..162fd95945 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -300,20 +300,19 @@ status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, cons if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) { continue; } - if (0 != planeLayoutComponent.offsetInBits % 8) { - unlock(bufferHandle); - return BAD_VALUE; - } - uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes + - (planeLayoutComponent.offsetInBits / 8); + uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes; + + // Note that `offsetInBits` may not be a multiple of 8 for packed formats (e.g. P010) + // but we still want to point to the start of the first byte. + tmpData += (planeLayoutComponent.offsetInBits / 8); + uint64_t sampleIncrementInBytes; auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value); switch (type) { case PlaneLayoutComponentType::Y: - if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) || - (planeLayout.sampleIncrementInBits != 8)) { + if ((ycbcr.y != nullptr) || (planeLayout.sampleIncrementInBits % 8 != 0)) { unlock(bufferHandle); return BAD_VALUE; } @@ -329,7 +328,8 @@ status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, cons } sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8; - if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) { + if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2) && + (sampleIncrementInBytes != 4)) { unlock(bufferHandle); return BAD_VALUE; } @@ -939,7 +939,7 @@ status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ost << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested) << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier - << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) + << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) << std::dec << ", compressed: "; if (less) { diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 516aad824e..b780770b86 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -93,7 +93,6 @@ cc_test { cflags: ["-Wall", "-Werror"], shared_libs: [ "libbinder", - "libgui", "liblog", "libui", "libutils", diff --git a/libs/ui/tests/GraphicBufferOverBinder_test.cpp b/libs/ui/tests/GraphicBufferOverBinder_test.cpp index 126a945a0a..4c9d574ef8 100644 --- a/libs/ui/tests/GraphicBufferOverBinder_test.cpp +++ b/libs/ui/tests/GraphicBufferOverBinder_test.cpp @@ -20,9 +20,6 @@ #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <gtest/gtest.h> -#include <gui/BufferQueue.h> -#include <gui/IGraphicBufferConsumer.h> -#include <gui/IGraphicBufferProducer.h> #include <ui/GraphicBuffer.h> #include <utils/Log.h> diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp index 749c568457..980b08ba36 100644 --- a/libs/vibrator/ExternalVibrationUtils.cpp +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -56,6 +56,36 @@ float getHapticMaxAmplitudeRatio(HapticScale scale) { } } +void applyHapticScale(float* buffer, size_t length, HapticScale scale) { + if (scale == HapticScale::MUTE) { + memset(buffer, 0, length * sizeof(float)); + return; + } + if (scale == HapticScale::NONE) { + return; + } + float gamma = getHapticScaleGamma(scale); + float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale); + for (size_t i = 0; i < length; i++) { + float sign = buffer[i] >= 0 ? 1.0 : -1.0; + buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + } +} + +void clipHapticData(float* buffer, size_t length, float limit) { + if (isnan(limit) || limit == 0) { + return; + } + limit = fabsf(limit); + for (size_t i = 0; i < length; i++) { + float sign = buffer[i] >= 0 ? 1.0 : -1.0; + if (fabsf(buffer[i]) > limit) { + buffer[i] = limit * sign; + } + } +} + } // namespace bool isValidHapticScale(HapticScale scale) { @@ -71,21 +101,11 @@ bool isValidHapticScale(HapticScale scale) { return false; } -void scaleHapticData(float* buffer, size_t length, HapticScale scale) { - if (!isValidHapticScale(scale) || scale == HapticScale::NONE) { - return; - } - if (scale == HapticScale::MUTE) { - memset(buffer, 0, length * sizeof(float)); - return; - } - float gamma = getHapticScaleGamma(scale); - float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale); - for (size_t i = 0; i < length; i++) { - float sign = buffer[i] >= 0 ? 1.0 : -1.0; - buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) - * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; +void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit) { + if (isValidHapticScale(scale)) { + applyHapticScale(buffer, length, scale); } + clipHapticData(buffer, length, limit); } } // namespace android::os diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index 20045d0769..84357fcb35 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -32,7 +32,11 @@ enum class HapticScale { bool isValidHapticScale(HapticScale scale); -void scaleHapticData(float* buffer, size_t length, HapticScale scale); +/* Scales the haptic data in given buffer using the selected HapticScale and ensuring no absolute + * value will be larger than the absolute of given limit. + * The limit will be ignored if it is NaN or zero. + */ +void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit); } // namespace android::os diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp index d4538f12a2..fa449aeb55 100644 --- a/libs/vr/libbroadcastring/Android.bp +++ b/libs/vr/libbroadcastring/Android.bp @@ -9,7 +9,7 @@ package { cc_library_static { name: "libbroadcastring", - clang: true, + cflags: [ "-Wall", "-Wextra", diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp index a8a847664f..de7d8f879b 100644 --- a/libs/vr/libvrflinger/vr_flinger.cpp +++ b/libs/vr/libvrflinger/vr_flinger.cpp @@ -17,6 +17,7 @@ #include <processgroup/sched_policy.h> #include <sys/prctl.h> #include <sys/resource.h> +#include <utils/ThreadDefs.h> #include <functional> diff --git a/opengl/OWNERS b/opengl/OWNERS index a9bd4bb2e2..379f7638f0 100644 --- a/opengl/OWNERS +++ b/opengl/OWNERS @@ -1,6 +1,11 @@ +abdolrashidi@google.com +cclao@google.com chrisforbes@google.com cnorthrop@google.com ianelliott@google.com jessehall@google.com +lfy@google.com lpy@google.com -timvp@google.com +romanl@google.com +vantablack@google.com +yuxinhu@google.com diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 6612a932a0..73e57495e6 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -73,6 +73,7 @@ cc_defaults { "libui", "lib-platform-compat-native-api", "server_configurable_flags", + "InputFlingerProperties", ], static_libs: [ "libattestation", @@ -95,6 +96,7 @@ cc_library_shared { "libinputflinger_base", "libinputreporter", "libinputreader", + "libgui", ], static_libs: [ "libinputdispatcher", diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index a50e5c70f7..7b3658dfde 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -31,6 +31,10 @@ namespace android { +using gui::FocusRequest; +using gui::WindowInfo; +using gui::WindowInfoHandle; + static int32_t exceptionCodeFromStatusT(status_t status) { switch (status) { case OK: @@ -110,33 +114,6 @@ sp<InputDispatcherInterface> InputManager::getDispatcher() { return mDispatcher; } -class BinderWindowHandle : public InputWindowHandle { -public: - BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; } - - bool updateInfo() override { - return true; - } -}; - -binder::Status InputManager::setInputWindows( - const std::vector<InputWindowInfo>& infos, - const sp<ISetInputWindowsListener>& setInputWindowsListener) { - std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay; - - std::vector<sp<InputWindowHandle>> handles; - for (const auto& info : infos) { - handlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>()); - handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info)); - } - mDispatcher->setInputWindows(handlesPerDisplay); - - if (setInputWindowsListener) { - setInputWindowsListener->onSetInputWindowsFinished(); - } - return binder::Status::ok(); -} - // Used by tests only. binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { IPCThreadState* ipc = IPCThreadState::self(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 49bea132c8..4c07c22154 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -26,7 +26,6 @@ #include <InputDispatcherInterface.h> #include <InputDispatcherPolicyInterface.h> -#include <android/os/ISetInputWindowsListener.h> #include <input/Input.h> #include <input/InputTransport.h> @@ -38,7 +37,6 @@ #include <utils/Vector.h> using android::os::BnInputFlinger; -using android::os::ISetInputWindowsListener; namespace android { class InputChannel; @@ -104,13 +102,9 @@ public: sp<InputDispatcherInterface> getDispatcher() override; status_t dump(int fd, const Vector<String16>& args) override; - binder::Status setInputWindows( - const std::vector<InputWindowInfo>& handles, - const sp<ISetInputWindowsListener>& setInputWindowsListener) override; - binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; - binder::Status setFocusedWindow(const FocusRequest&) override; + binder::Status setFocusedWindow(const gui::FocusRequest&) override; private: sp<InputReaderInterface> mReader; diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index d34482f506..05ef489133 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -19,9 +19,9 @@ //#define LOG_NDEBUG 0 #include "InputReaderBase.h" +#include <ftl/NamedEnum.h> #include "input/DisplayViewport.h" #include "input/Input.h" -#include "input/NamedEnum.h" #include <android/log.h> #include <android-base/stringprintf.h> diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index aa8cc302c7..6ce0313929 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -18,8 +18,11 @@ #include <android/os/IInputConstants.h> #include <binder/Binder.h> +#include <gui/constants.h> #include "../dispatcher/InputDispatcher.h" +using android::gui::WindowInfo; +using android::gui::WindowInfoHandle; using android::os::IInputConstants; using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; @@ -173,7 +176,7 @@ protected: PreallocatedInputEventFactory mEventFactory; }; -class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver { +class FakeWindowHandle : public WindowInfoHandle, public FakeInputReceiver { public: static const int32_t WIDTH = 200; static const int32_t HEIGHT = 200; @@ -182,13 +185,14 @@ public: const sp<InputDispatcher>& dispatcher, const std::string name) : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) { inputApplicationHandle->updateInfo(); + updateInfo(); mInfo.applicationInfo = *inputApplicationHandle->getInfo(); } - virtual bool updateInfo() override { + void updateInfo() { mInfo.token = mClientChannel->getConnectionToken(); mInfo.name = "FakeWindowHandle"; - mInfo.type = InputWindowInfo::Type::APPLICATION; + mInfo.type = WindowInfo::Type::APPLICATION; mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; mInfo.frameLeft = mFrame.left; mInfo.frameTop = mFrame.top; @@ -204,8 +208,6 @@ public: mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.displayId = ADISPLAY_ID_DEFAULT; - - return true; } protected: @@ -234,8 +236,8 @@ static MotionEvent generateMotionEvent() { /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, currentTime, currentTime, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); return event; } diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 1b3888b340..171f2b5ce8 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -64,9 +64,11 @@ cc_defaults { "libstatspull", "libstatssocket", "libui", + "libgui", "libutils", "lib-platform-compat-native-api", "server_configurable_flags", + "InputFlingerProperties", ], static_libs: [ "libattestation", diff --git a/services/inputflinger/dispatcher/DragState.cpp b/services/inputflinger/dispatcher/DragState.cpp index 2e2df43009..e1844a4a3a 100644 --- a/services/inputflinger/dispatcher/DragState.cpp +++ b/services/inputflinger/dispatcher/DragState.cpp @@ -16,9 +16,7 @@ #include "DragState.h" #include <android-base/stringprintf.h> -#include <input/InputWindow.h> -using android::InputWindowHandle; using android::base::StringPrintf; namespace android::inputdispatcher { diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h index 06453d8eee..b3c5709cbc 100644 --- a/services/inputflinger/dispatcher/DragState.h +++ b/services/inputflinger/dispatcher/DragState.h @@ -17,22 +17,22 @@ #ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H #define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H +#include <gui/WindowInfo.h> #include <utils/RefBase.h> #include <string> namespace android { -class InputWindowHandle; - namespace inputdispatcher { + struct DragState { - DragState(const sp<android::InputWindowHandle>& windowHandle) : dragWindow(windowHandle) {} + DragState(const sp<android::gui::WindowInfoHandle>& windowHandle) : dragWindow(windowHandle) {} void dump(std::string& dump, const char* prefix = ""); // The window being dragged. - const sp<InputWindowHandle> dragWindow; + const sp<android::gui::WindowInfoHandle> dragWindow; // The last drag hover window which could receive the drag event. - sp<InputWindowHandle> dragHoverWindowHandle; + sp<android::gui::WindowInfoHandle> dragHoverWindowHandle; // Indicates the if received first event to check for button state. bool isStartDrag = false; // Indicate if the stylus button is down at the start of the drag. diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 5c3747e2ec..82b4fe476e 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -296,12 +296,14 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, int2 displaySize) + ui::Transform transform, float globalScaleFactor, + uint32_t displayOrientation, int2 displaySize) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), transform(transform), globalScaleFactor(globalScaleFactor), + displayOrientation(displayOrientation), displaySize(displaySize), deliveryTime(0), resolvedAction(0), diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 6f1dfadf59..ffe3bb691c 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -20,8 +20,8 @@ #include "InjectionState.h" #include "InputTarget.h" +#include <gui/InputApplication.h> #include <input/Input.h> -#include <input/InputApplication.h> #include <stdint.h> #include <utils/Timers.h> #include <functional> @@ -215,6 +215,7 @@ struct DispatchEntry { int32_t targetFlags; ui::Transform transform; float globalScaleFactor; + uint32_t displayOrientation; int2 displaySize; // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, // and will be undefined before that. @@ -228,7 +229,8 @@ struct DispatchEntry { int32_t resolvedFlags; DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, int2 displaySize); + ui::Transform transform, float globalScaleFactor, uint32_t displayOrientation, + int2 displaySize); inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp index fb194355ba..4a75773201 100644 --- a/services/inputflinger/dispatcher/FocusResolver.cpp +++ b/services/inputflinger/dispatcher/FocusResolver.cpp @@ -27,12 +27,15 @@ static constexpr bool DEBUG_FOCUS = false; #include <android-base/stringprintf.h> #include <binder/Binder.h> -#include <input/InputWindow.h> -#include <input/NamedEnum.h> +#include <ftl/NamedEnum.h> +#include <gui/WindowInfo.h> #include <log/log.h> #include "FocusResolver.h" +using android::gui::FocusRequest; +using android::gui::WindowInfoHandle; + namespace android::inputdispatcher { sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const { @@ -52,7 +55,7 @@ std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) { * we will check if the previous focus request is eligible to receive focus. */ std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows( - int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) { + int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) { std::string removeFocusReason; // Check if the currently focused window is still focusable. @@ -87,7 +90,7 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows( } std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( - const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) { + const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) { const int32_t displayId = request.displayId; const sp<IBinder> currentFocus = getFocusedWindowToken(displayId); if (currentFocus == request.token) { @@ -136,11 +139,11 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( } FocusResolver::Focusability FocusResolver::isTokenFocusable( - const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) { + const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows) { bool allWindowsAreFocusable = true; bool visibleWindowFound = false; bool windowFound = false; - for (const sp<InputWindowHandle>& window : windows) { + for (const sp<WindowInfoHandle>& window : windows) { if (window->getToken() != token) { continue; } diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h index afe16b3b45..1d6cd9a5fa 100644 --- a/services/inputflinger/dispatcher/FocusResolver.h +++ b/services/inputflinger/dispatcher/FocusResolver.h @@ -20,9 +20,9 @@ #include <optional> #include <unordered_map> -#include <android/FocusRequest.h> +#include <android/gui/FocusRequest.h> #include <binder/Binder.h> -#include <input/InputWindow.h> +#include <gui/WindowInfo.h> namespace android::inputdispatcher { @@ -58,9 +58,10 @@ public: std::string reason; }; std::optional<FocusResolver::FocusChanges> setInputWindows( - int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows); + int32_t displayId, const std::vector<sp<android::gui::WindowInfoHandle>>& windows); std::optional<FocusResolver::FocusChanges> setFocusedWindow( - const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows); + const android::gui::FocusRequest& request, + const std::vector<sp<android::gui::WindowInfoHandle>>& windows); // Display has been removed from the system, clean up old references. void displayRemoved(int32_t displayId); @@ -87,8 +88,9 @@ private: // we expect the focusability of the windows to match since its hard to reason why one window // can receive focus events and the other cannot when both are backed by the same input channel. // - static Focusability isTokenFocusable(const sp<IBinder>& token, - const std::vector<sp<InputWindowHandle>>& windows); + static Focusability isTokenFocusable( + const sp<IBinder>& token, + const std::vector<sp<android::gui::WindowInfoHandle>>& windows); // Focus tracking for keys, trackball, etc. A window token can be associated with one or // more InputWindowHandles. If a window is mirrored, the window and its mirror will share @@ -99,7 +101,7 @@ private: // This map will store the focus request per display. When the input window handles are updated, // the current request will be checked to see if it can be processed at that time. - std::unordered_map<int32_t /* displayId */, FocusRequest> mFocusRequestByDisplay; + std::unordered_map<int32_t /* displayId */, android::gui::FocusRequest> mFocusRequestByDisplay; // Last reason for not granting a focus request. This is used to add more debug information // in the event logs. @@ -108,7 +110,7 @@ private: std::optional<FocusResolver::FocusChanges> updateFocusedWindow( int32_t displayId, const std::string& reason, const sp<IBinder>& token, const std::string& tokenName = ""); - std::optional<FocusRequest> getFocusRequest(int32_t displayId); + std::optional<android::gui::FocusRequest> getFocusRequest(int32_t displayId); }; } // namespace android::inputdispatcher
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 6e9430a975..d53495f9c8 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -47,6 +47,7 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; // Log debug messages about hover events. #define DEBUG_HOVER 0 +#include <InputFlingerProperties.sysprop.h> #include <android-base/chrono_utils.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -54,8 +55,8 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; #include <binder/Binder.h> #include <binder/IServiceManager.h> #include <com/android/internal/compat/IPlatformCompatNative.h> +#include <gui/SurfaceComposerClient.h> #include <input/InputDevice.h> -#include <input/InputWindow.h> #include <log/log.h> #include <log/log_event_list.h> #include <powermanager/PowerManager.h> @@ -81,6 +82,10 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; using android::base::HwTimeoutMultiplier; using android::base::Result; using android::base::StringPrintf; +using android::gui::FocusRequest; +using android::gui::TouchOcclusionMode; +using android::gui::WindowInfo; +using android::gui::WindowInfoHandle; using android::os::BlockUntrustedTouchesMode; using android::os::IInputConstants; using android::os::InputEventInjectionResult; @@ -93,7 +98,8 @@ namespace android::inputdispatcher { // coordinates and SurfaceFlinger includes the display rotation in the input window transforms. static bool isPerWindowInputRotationEnabled() { static const bool PER_WINDOW_INPUT_ROTATION = - base::GetBoolProperty("persist.debug.per_window_input_rotation", false); + sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false); + return PER_WINDOW_INPUT_ROTATION; } @@ -290,7 +296,7 @@ static V getValueByKey(const std::unordered_map<K, V>& map, K key) { return it != map.end() ? it->second : V{}; } -static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) { +static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) { if (first == second) { return true; } @@ -302,7 +308,7 @@ static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWind return first->getToken() == second->getToken(); } -static bool haveSameApplicationToken(const InputWindowInfo* first, const InputWindowInfo* second) { +static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) { if (first == nullptr || second == nullptr) { return false; } @@ -319,13 +325,14 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp int32_t inputTargetFlags) { if (eventEntry->type == EventEntry::Type::MOTION) { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); - if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) == 0) { + if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) || + (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) { const ui::Transform identityTransform; - // Use identity transform for events that are not pointer events because their axes - // values do not represent on-screen coordinates, so they should not have any window - // transformations applied to them. + // Use identity transform for joystick and position-based (touchpad) events because they + // don't depend on the window transform. return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform, 1.0f /*globalScaleFactor*/, + inputTarget.displayOrientation, inputTarget.displaySize); } } @@ -334,6 +341,7 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, inputTarget.globalScaleFactor, + inputTarget.displayOrientation, inputTarget.displaySize); } @@ -386,6 +394,7 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, firstPointerTransform, inputTarget.globalScaleFactor, + inputTarget.displayOrientation, inputTarget.displaySize); return dispatchEntry; } @@ -550,6 +559,10 @@ InputDispatcher::~InputDispatcher() { } } +void InputDispatcher::onFirstRef() { + SurfaceComposerClient::getDefault()->addWindowInfosListener(this); +} + status_t InputDispatcher::start() { if (mThread) { return ALREADY_EXISTS; @@ -623,7 +636,7 @@ void InputDispatcher::processNoFocusedWindowAnrLocked() { return; // The focused application has changed. } - const sp<InputWindowHandle>& focusedWindowHandle = + const sp<WindowInfoHandle>& focusedWindowHandle = getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId); if (focusedWindowHandle != nullptr) { return; // We now have a focused window. No need for ANR. @@ -672,7 +685,7 @@ nsecs_t InputDispatcher::processAnrsLocked() { } std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) { - sp<InputWindowHandle> window = getWindowHandleLocked(token); + sp<WindowInfoHandle> window = getWindowHandleLocked(token); if (window != nullptr) { return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } @@ -884,7 +897,7 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = static_cast<int32_t>( motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp<InputWindowHandle> touchedWindowHandle = + sp<WindowInfoHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, nullptr); if (touchedWindowHandle != nullptr && touchedWindowHandle->getApplicationToken() != @@ -989,29 +1002,29 @@ void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) { } } -sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, - int32_t y, TouchState* touchState, - bool addOutsideTargets, - bool addPortalWindows, - bool ignoreDragWindow) { +sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, + int32_t y, TouchState* touchState, + bool addOutsideTargets, + bool addPortalWindows, + bool ignoreDragWindow) { if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) { LOG_ALWAYS_FATAL( "Must provide a valid touch state if adding portal windows or outside targets"); } // Traverse windows from front to back to find touched window. - const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); - for (const sp<InputWindowHandle>& windowHandle : windowHandles) { + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); + for (const sp<WindowInfoHandle>& windowHandle : windowHandles) { if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) { continue; } - const InputWindowInfo* windowInfo = windowHandle->getInfo(); + const WindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId == displayId) { auto flags = windowInfo->flags; if (windowInfo->visible) { - if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) { - bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) && - !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + if (!flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) { + bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) && + !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL); if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { int32_t portalToDisplayId = windowInfo->portalToDisplayId; if (portalToDisplayId != ADISPLAY_ID_NONE && @@ -1028,7 +1041,7 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display } } - if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) { + if (addOutsideTargets && flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) { touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0)); @@ -1040,13 +1053,13 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display } std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const { + int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) const { std::vector<TouchedMonitor> touchedMonitors; std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId); addGestureMonitors(monitors, touchedMonitors); - for (const sp<InputWindowHandle>& portalWindow : portalWindows) { - const InputWindowInfo* windowInfo = portalWindow->getInfo(); + for (const sp<WindowInfoHandle>& portalWindow : portalWindows) { + const WindowInfo* windowInfo = portalWindow->getInfo(); monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId); addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft, -windowInfo->frameTop); @@ -1609,7 +1622,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< // The event has gone through these portal windows, so we add monitoring targets of // the corresponding displays as well. for (size_t i = 0; i < state.portalWindows.size(); i++) { - const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo(); + const WindowInfo* windowInfo = state.portalWindows[i]->getInfo(); addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId, -windowInfo->frameLeft, -windowInfo->frameTop); } @@ -1627,7 +1640,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< return true; } -void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle, +void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowHandle, bool isExiting, const MotionEntry& motionEntry) { // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid. @@ -1807,7 +1820,7 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( std::string reason; int32_t displayId = getTargetDisplayId(entry); - sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); + sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); std::shared_ptr<InputApplicationHandle> focusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); @@ -1820,6 +1833,11 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( return InputEventInjectionResult::FAILED; } + // Drop key events if requested by input feature + if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) { + return InputEventInjectionResult::FAILED; + } + // Compatibility behavior: raise ANR if there is a focused application, but no focused window. // Only start counting when we have a focused event to dispatch. The ANR is canceled if we // start interacting with another application via touch (app switch). This code can be removed @@ -1934,8 +1952,8 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Update the touch state as needed based on the properties of the touch event. InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING; InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; - sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle); - sp<InputWindowHandle> newTouchedWindowHandle; + sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle); + sp<WindowInfoHandle> newTouchedWindowHandle; // Copy current touch state into tempTouchState. // This state will be used to update mTouchStatesByDisplay at the end of this function. @@ -2008,10 +2026,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isDown /*addOutsideTargets*/, true /*addPortalWindows*/); - std::vector<TouchedMonitor> newGestureMonitors = isDown - ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows) - : std::vector<TouchedMonitor>{}; - // Figure out whether splitting will be allowed for this window. if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { @@ -2066,8 +2080,15 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } } - // Also don't send the new touch event to unresponsive gesture monitors - newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors); + // Drop touch events if requested by input feature + if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) { + newTouchedWindowHandle = nullptr; + } + + const std::vector<TouchedMonitor> newGestureMonitors = isDown + ? selectResponsiveMonitorsLocked( + findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)) + : tempTouchState.gestureMonitors; if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { ALOGI("Dropping event because there is no touchable window or gesture monitor at " @@ -2103,9 +2124,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( pointerIds.markBit(pointerId); } tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + } else if (tempTouchState.windows.empty()) { + // If no window is touched, set split to true. This will allow the next pointer down to + // be delivered to a new window which supports split touch. + tempTouchState.split = true; + } + if (isDown) { + tempTouchState.addGestureMonitors(newGestureMonitors); } - - tempTouchState.addGestureMonitors(newGestureMonitors); } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ @@ -2128,9 +2154,16 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp<InputWindowHandle> oldTouchedWindowHandle = + sp<WindowInfoHandle> oldTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState); + + // Drop touch events if requested by input feature + if (newTouchedWindowHandle != nullptr && + shouldDropInput(entry, newTouchedWindowHandle)) { + newTouchedWindowHandle = nullptr; + } + if (oldTouchedWindowHandle != newTouchedWindowHandle && oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { if (DEBUG_FOCUS) { @@ -2227,15 +2260,15 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Check whether windows listening for outside touches are owned by the same UID. If it is // set the policy flag that we will not reveal coordinate information to this window. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - sp<InputWindowHandle> foregroundWindowHandle = + sp<WindowInfoHandle> foregroundWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle) { const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle; - if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) { - tempTouchState.addOrUpdateWindow(inputWindowHandle, + sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle; + if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) { + tempTouchState.addOrUpdateWindow(windowInfoHandle, InputTarget::FLAG_ZERO_COORDS, BitSet32(0)); } @@ -2251,15 +2284,15 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // engine only supports touch events. We would need to add a mechanism similar // to View.onGenericMotionEvent to enable wallpapers to handle these events. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - sp<InputWindowHandle> foregroundWindowHandle = + sp<WindowInfoHandle> foregroundWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) { - const std::vector<sp<InputWindowHandle>>& windowHandles = + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); - for (const sp<InputWindowHandle>& windowHandle : windowHandles) { - const InputWindowInfo* info = windowHandle->getInfo(); + for (const sp<WindowInfoHandle>& windowHandle : windowHandles) { + const WindowInfo* info = windowHandle->getInfo(); if (info->displayId == displayId && - windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) { + windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) { tempTouchState .addOrUpdateWindow(windowHandle, InputTarget::FLAG_WINDOW_IS_OBSCURED | @@ -2378,7 +2411,7 @@ Failed: } void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) { - const sp<InputWindowHandle> dropWindow = + const sp<WindowInfoHandle> dropWindow = findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, false /*addOutsideTargets*/, false /*addPortalWindows*/, true /*ignoreDragWindow*/); @@ -2413,7 +2446,7 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { return; } - const sp<InputWindowHandle> hoverWindowHandle = + const sp<WindowInfoHandle> hoverWindowHandle = findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, false /*addOutsideTargets*/, false /*addPortalWindows*/, true /*ignoreDragWindow*/); @@ -2438,7 +2471,7 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { } } -void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, +void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) { std::vector<InputTarget>::iterator it = @@ -2448,7 +2481,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH windowHandle->getToken(); }); - const InputWindowInfo* windowInfo = windowHandle->getInfo(); + const WindowInfo* windowInfo = windowHandle->getInfo(); if (it == inputTargets.end()) { InputTarget inputTarget; @@ -2461,6 +2494,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH inputTarget.inputChannel = inputChannel; inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; + inputTarget.displayOrientation = windowInfo->displayOrientation; inputTarget.displaySize = int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight); inputTargets.push_back(inputTarget); @@ -2499,7 +2533,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO inputTargets.push_back(target); } -bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, +bool InputDispatcher::checkInjectionPermission(const sp<WindowInfoHandle>& windowHandle, const InjectionState* injectionState) { if (injectionState && (windowHandle == nullptr || @@ -2524,8 +2558,8 @@ bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& wind * another window handle. We only check a few preconditions. Actually * checking the bounds is left to the caller. */ -static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle, - const sp<InputWindowHandle>& otherHandle) { +static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle, + const sp<WindowInfoHandle>& otherHandle) { // Compare by token so cloned layers aren't counted if (haveSameToken(windowHandle, otherHandle)) { return false; @@ -2534,8 +2568,7 @@ static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle, auto otherInfo = otherHandle->getInfo(); if (!otherInfo->visible) { return false; - } else if (otherInfo->alpha == 0 && - otherInfo->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) { + } else if (otherInfo->alpha == 0 && otherInfo->flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) { // Those act as if they were invisible, so we don't need to flag them. // We do want to potentially flag touchable windows even if they have 0 // opacity, since they can consume touches and alter the effects of the @@ -2573,20 +2606,20 @@ static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle, * If neither of those is true, then it means the touch can be allowed. */ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked( - const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const { - const InputWindowInfo* windowInfo = windowHandle->getInfo(); + const sp<WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const { + const WindowInfo* windowInfo = windowHandle->getInfo(); int32_t displayId = windowInfo->displayId; - const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); TouchOcclusionInfo info; info.hasBlockingOcclusion = false; info.obscuringOpacity = 0; info.obscuringUid = -1; std::map<int32_t, float> opacityByUid; - for (const sp<InputWindowHandle>& otherHandle : windowHandles) { + for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { if (windowHandle == otherHandle) { break; // All future windows are below us. Exit early. } - const InputWindowInfo* otherInfo = otherHandle->getInfo(); + const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) && !haveSameApplicationToken(windowInfo, otherInfo)) { if (DEBUG_TOUCH_OCCLUSION) { @@ -2625,7 +2658,7 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo return info; } -std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info, +std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info, bool isTouchedWindow) const { return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, " @@ -2659,15 +2692,15 @@ bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionIn return true; } -bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, +bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const { int32_t displayId = windowHandle->getInfo()->displayId; - const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); - for (const sp<InputWindowHandle>& otherHandle : windowHandles) { + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); + for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { if (windowHandle == otherHandle) { break; // All future windows are below us. Exit early. } - const InputWindowInfo* otherInfo = otherHandle->getInfo(); + const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y)) { return true; @@ -2676,15 +2709,15 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& return false; } -bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const { +bool InputDispatcher::isWindowObscuredLocked(const sp<WindowInfoHandle>& windowHandle) const { int32_t displayId = windowHandle->getInfo()->displayId; - const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - for (const sp<InputWindowHandle>& otherHandle : windowHandles) { + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); + const WindowInfo* windowInfo = windowHandle->getInfo(); + for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { if (windowHandle == otherHandle) { break; // All future windows are below us. Exit early. } - const InputWindowInfo* otherInfo = otherHandle->getInfo(); + const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->overlaps(windowInfo)) { return true; @@ -2694,8 +2727,7 @@ bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& window } std::string InputDispatcher::getApplicationWindowLabel( - const InputApplicationHandle* applicationHandle, - const sp<InputWindowHandle>& windowHandle) { + const InputApplicationHandle* applicationHandle, const sp<WindowInfoHandle>& windowHandle) { if (applicationHandle != nullptr) { if (windowHandle != nullptr) { return applicationHandle->getName() + " - " + windowHandle->getName(); @@ -2718,10 +2750,10 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { return; } int32_t displayId = getTargetDisplayId(eventEntry); - sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); + sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); if (focusedWindowHandle != nullptr) { - const InputWindowInfo* info = focusedWindowHandle->getInfo(); - if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) { + const WindowInfo* info = focusedWindowHandle->getInfo(); + if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) { #if DEBUG_DISPATCH_CYCLE ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); #endif @@ -3176,6 +3208,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry.xPrecision, motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, + dispatchEntry->displayOrientation, dispatchEntry->displaySize.x, dispatchEntry->displaySize.y, motionEntry.downTime, motionEntry.eventTime, @@ -3492,10 +3525,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( #endif InputTarget target; - sp<InputWindowHandle> windowHandle = + sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { - const InputWindowInfo* windowInfo = windowHandle->getInfo(); + const WindowInfo* windowInfo = windowHandle->getInfo(); target.setDefaultPointerTransform(windowInfo->transform); target.globalScaleFactor = windowInfo->globalScaleFactor; } @@ -3559,10 +3592,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( #endif InputTarget target; - sp<InputWindowHandle> windowHandle = + sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { - const InputWindowInfo* windowInfo = windowHandle->getInfo(); + const WindowInfo* windowInfo = windowHandle->getInfo(); target.setDefaultPointerTransform(windowInfo->transform); target.globalScaleFactor = windowInfo->globalScaleFactor; } @@ -3888,8 +3921,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->action, args->actionButton, args->flags, args->edgeFlags, args->metaState, args->buttonState, args->classification, transform, args->xPrecision, args->yPrecision, args->xCursorPosition, - args->yCursorPosition, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, args->downTime, args->eventTime, + args->yCursorPosition, ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, + INVALID_DISPLAY_SIZE, args->downTime, args->eventTime, args->pointerCount, args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; @@ -4340,22 +4373,22 @@ void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) { } } -const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked( +const std::vector<sp<WindowInfoHandle>>& InputDispatcher::getWindowHandlesLocked( int32_t displayId) const { - static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES; + static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES; auto it = mWindowHandlesByDisplay.find(displayId); return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES; } -sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( +sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked( const sp<IBinder>& windowHandleToken) const { if (windowHandleToken == nullptr) { return nullptr; } for (auto& it : mWindowHandlesByDisplay) { - const std::vector<sp<InputWindowHandle>>& windowHandles = it.second; - for (const sp<InputWindowHandle>& windowHandle : windowHandles) { + const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second; + for (const sp<WindowInfoHandle>& windowHandle : windowHandles) { if (windowHandle->getToken() == windowHandleToken) { return windowHandle; } @@ -4364,13 +4397,13 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( return nullptr; } -sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken, - int displayId) const { +sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken, + int displayId) const { if (windowHandleToken == nullptr) { return nullptr; } - for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) { + for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(displayId)) { if (windowHandle->getToken() == windowHandleToken) { return windowHandle; } @@ -4378,11 +4411,11 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& return nullptr; } -sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( - const sp<InputWindowHandle>& windowHandle) const { +sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked( + const sp<WindowInfoHandle>& windowHandle) const { for (auto& it : mWindowHandlesByDisplay) { - const std::vector<sp<InputWindowHandle>>& windowHandles = it.second; - for (const sp<InputWindowHandle>& handle : windowHandles) { + const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second; + for (const sp<WindowInfoHandle>& handle : windowHandles) { if (handle->getId() == windowHandle->getId() && handle->getToken() == windowHandle->getToken()) { if (windowHandle->getInfo()->displayId != it.first) { @@ -4398,15 +4431,15 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( return nullptr; } -sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const { +sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const { sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId); return getWindowHandleLocked(focusedToken, displayId); } -bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const { +bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const { sp<Connection> connection = getConnectionLocked(windowHandle.getToken()); const bool noInputChannel = - windowHandle.getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + windowHandle.getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); if (connection != nullptr && noInputChannel) { ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s", windowHandle.getName().c_str(), connection->inputChannel->getName().c_str()); @@ -4436,8 +4469,8 @@ std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked( } void InputDispatcher::updateWindowHandlesForDisplayLocked( - const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) { - if (inputWindowHandles.empty()) { + const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) { + if (windowInfoHandles.empty()) { // Remove all handles on a display if there are no windows left. mWindowHandlesByDisplay.erase(displayId); return; @@ -4445,26 +4478,21 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( // Since we compare the pointer of input window handles across window updates, we need // to make sure the handle object for the same window stays unchanged across updates. - const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId); - std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById; - for (const sp<InputWindowHandle>& handle : oldHandles) { + const std::vector<sp<WindowInfoHandle>>& oldHandles = getWindowHandlesLocked(displayId); + std::unordered_map<int32_t /*id*/, sp<WindowInfoHandle>> oldHandlesById; + for (const sp<WindowInfoHandle>& handle : oldHandles) { oldHandlesById[handle->getId()] = handle; } - std::vector<sp<InputWindowHandle>> newHandles; - for (const sp<InputWindowHandle>& handle : inputWindowHandles) { - if (!handle->updateInfo()) { - // handle no longer valid - continue; - } - - const InputWindowInfo* info = handle->getInfo(); + std::vector<sp<WindowInfoHandle>> newHandles; + for (const sp<WindowInfoHandle>& handle : windowInfoHandles) { + const WindowInfo* info = handle->getInfo(); if ((getInputChannelLocked(handle->getToken()) == nullptr && info->portalToDisplayId == ADISPLAY_ID_NONE)) { const bool noInputChannel = - info->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL); - const bool canReceiveInput = !info->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE) || - !info->flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE); + info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); + const bool canReceiveInput = !info->flags.test(WindowInfo::Flag::NOT_TOUCHABLE) || + !info->flags.test(WindowInfo::Flag::NOT_FOCUSABLE); if (canReceiveInput && !noInputChannel) { ALOGV("Window handle %s has no registered input channel", handle->getName().c_str()); @@ -4480,7 +4508,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) && (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) { - const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId()); + const sp<WindowInfoHandle>& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); } else { @@ -4493,7 +4521,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( } void InputDispatcher::setInputWindows( - const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) { + const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) { { // acquire lock std::scoped_lock _l(mLock); for (const auto& [displayId, handles] : handlesPerDisplay) { @@ -4512,19 +4540,19 @@ void InputDispatcher::setInputWindows( * For removed handle, check if need to send a cancel event if already in touch. */ void InputDispatcher::setInputWindowsLocked( - const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) { + const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) { if (DEBUG_FOCUS) { std::string windowList; - for (const sp<InputWindowHandle>& iwh : inputWindowHandles) { + for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) { windowList += iwh->getName() + " "; } ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL - for (const sp<InputWindowHandle>& window : inputWindowHandles) { + for (const sp<WindowInfoHandle>& window : windowInfoHandles) { const bool noInputWindow = - window->getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + window->getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); if (noInputWindow && window->getToken() != nullptr) { ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing", window->getName().c_str()); @@ -4533,18 +4561,18 @@ void InputDispatcher::setInputWindowsLocked( } // Copy old handles for release if they are no longer present. - const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); + const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); // Save the old windows' orientation by ID before it gets updated. std::unordered_map<int32_t, uint32_t> oldWindowOrientations; - for (const sp<InputWindowHandle>& handle : oldWindowHandles) { + for (const sp<WindowInfoHandle>& handle : oldWindowHandles) { oldWindowOrientations.emplace(handle->getId(), handle->getInfo()->transform.getOrientation()); } - updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); + updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); - const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); if (mLastHoverWindowHandle && std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) == windowHandles.end()) { @@ -4593,8 +4621,8 @@ void InputDispatcher::setInputWindowsLocked( if (isPerWindowInputRotationEnabled()) { // Determine if the orientation of any of the input windows have changed, and cancel all // pointer events if necessary. - for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) { - const sp<InputWindowHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle); + for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) { + const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle); if (newWindowHandle != nullptr && newWindowHandle->getInfo()->transform.getOrientation() != oldWindowOrientations[oldWindowHandle->getId()]) { @@ -4613,7 +4641,7 @@ void InputDispatcher::setInputWindowsLocked( // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. - for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) { + for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) { if (getWindowHandleLocked(oldWindowHandle) == nullptr) { if (DEBUG_FOCUS) { ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); @@ -4623,7 +4651,7 @@ void InputDispatcher::setInputWindowsLocked( // check for window flags when windows are going away. // TODO(b/157929241) : delete this. This is only needed temporarily // in order to gather some data about the flag usage - if (oldWindowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) { + if (oldWindowHandle->getInfo()->flags.test(WindowInfo::Flag::SLIPPERY)) { ALOGW("%s has FLAG_SLIPPERY. Please report this in b/157929241", oldWindowHandle->getName().c_str()); if (mCompatService != nullptr) { @@ -4801,6 +4829,18 @@ void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mod mBlockUntrustedTouchesMode = mode; } +std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked( + const sp<IBinder>& token) { + for (auto& [displayId, state] : mTouchStatesByDisplay) { + for (TouchedWindow& w : state.windows) { + if (w.windowHandle->getToken() == token) { + return std::make_pair(&state, &w); + } + } + } + return std::make_pair(nullptr, nullptr); +} + bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, bool isDragDrop) { if (fromToken == toToken) { @@ -4813,58 +4853,43 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< { // acquire lock std::scoped_lock _l(mLock); - sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken); - sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken); - if (fromWindowHandle == nullptr || toWindowHandle == nullptr) { - ALOGW("Cannot transfer focus because from or to window not found."); + // Find the target touch state and touched window by fromToken. + auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken); + if (state == nullptr || touchedWindow == nullptr) { + ALOGD("Focus transfer failed because from window is not being touched."); return false; } - if (DEBUG_FOCUS) { - ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", - fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); - } - if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { - if (DEBUG_FOCUS) { - ALOGD("Cannot transfer focus because windows are on different displays."); - } + + const int32_t displayId = state->displayId; + sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); + if (toWindowHandle == nullptr) { + ALOGW("Cannot transfer focus because to window not found."); return false; } - bool found = false; - for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) { - TouchState& state = pair.second; - for (size_t i = 0; i < state.windows.size(); i++) { - const TouchedWindow& touchedWindow = state.windows[i]; - if (touchedWindow.windowHandle == fromWindowHandle) { - int32_t oldTargetFlags = touchedWindow.targetFlags; - BitSet32 pointerIds = touchedWindow.pointerIds; - - state.windows.erase(state.windows.begin() + i); - - int32_t newTargetFlags = oldTargetFlags & - (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | - InputTarget::FLAG_DISPATCH_AS_IS); - state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); + if (DEBUG_FOCUS) { + ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", + touchedWindow->windowHandle->getName().c_str(), + toWindowHandle->getName().c_str()); + } - // Store the dragging window. - if (isDragDrop) { - mDragState = std::make_unique<DragState>(toWindowHandle); - } + // Erase old window. + int32_t oldTargetFlags = touchedWindow->targetFlags; + BitSet32 pointerIds = touchedWindow->pointerIds; + state->removeWindowByToken(fromToken); - found = true; - goto Found; - } - } - } - Found: + // Add new window. + int32_t newTargetFlags = oldTargetFlags & + (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | + InputTarget::FLAG_DISPATCH_AS_IS); + state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); - if (!found) { - if (DEBUG_FOCUS) { - ALOGD("Focus transfer failed because from window did not have focus."); - } - return false; + // Store the dragging window. + if (isDragDrop) { + mDragState = std::make_unique<DragState>(toWindowHandle); } + // Synthesize cancel for old window and down for new window. sp<Connection> fromConnection = getConnectionLocked(fromToken); sp<Connection> toConnection = getConnectionLocked(toToken); if (fromConnection != nullptr && toConnection != nullptr) { @@ -4892,27 +4917,20 @@ bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) { { // acquire lock std::scoped_lock _l(mLock); - sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken); - if (toWindowHandle == nullptr) { - ALOGW("Could not find window associated with token=%p", destChannelToken.get()); + auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(), + [](const auto& pair) { return pair.second.windows.size() == 1; }); + if (it == mTouchStatesByDisplay.end()) { + ALOGW("Cannot transfer touch state because there is no exact window being touched"); return false; } - - const int32_t displayId = toWindowHandle->getInfo()->displayId; - - auto touchStateIt = mTouchStatesByDisplay.find(displayId); - if (touchStateIt == mTouchStatesByDisplay.end()) { - ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched", - displayId); + const int32_t displayId = it->first; + sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId); + if (toWindowHandle == nullptr) { + ALOGW("Could not find window associated with token=%p", destChannelToken.get()); return false; } - TouchState& state = touchStateIt->second; - if (state.windows.size() != 1) { - ALOGW("Cannot transfer touch state because there are %zu windows being touched", - state.windows.size()); - return false; - } + TouchState& state = it->second; const TouchedWindow& touchedWindow = state.windows[0]; fromToken = touchedWindow.windowHandle->getToken(); } // release lock @@ -4959,7 +4977,7 @@ std::string InputDispatcher::dumpPointerCaptureStateLocked() { std::string windowName = "None"; if (mWindowTokenWithPointerCapture) { - const sp<InputWindowHandle> captureWindowHandle = + const sp<WindowInfoHandle> captureWindowHandle = getWindowHandleLocked(mWindowTokenWithPointerCapture); windowName = captureWindowHandle ? captureWindowHandle->getName().c_str() : "token has capture without window"; @@ -5015,7 +5033,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!state.portalWindows.empty()) { dump += INDENT3 "Portal windows:\n"; for (size_t i = 0; i < state.portalWindows.size(); i++) { - const sp<InputWindowHandle> portalWindowHandle = state.portalWindows[i]; + const sp<WindowInfoHandle> portalWindowHandle = state.portalWindows[i]; dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i, portalWindowHandle->getName().c_str()); } @@ -5032,13 +5050,13 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!mWindowHandlesByDisplay.empty()) { for (auto& it : mWindowHandlesByDisplay) { - const std::vector<sp<InputWindowHandle>> windowHandles = it.second; + const std::vector<sp<WindowInfoHandle>> windowHandles = it.second; dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first); if (!windowHandles.empty()) { dump += INDENT2 "Windows:\n"; for (size_t i = 0; i < windowHandles.size(); i++) { - const sp<InputWindowHandle>& windowHandle = windowHandles[i]; - const InputWindowInfo* windowInfo = windowHandle->getInfo(); + const sp<WindowInfoHandle>& windowHandle = windowHandles[i]; + const WindowInfo* windowInfo = windowHandle->getInfo(); dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, " "portalToDisplayId=%d, paused=%s, focusable=%s, " @@ -5066,12 +5084,13 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { windowInfo->inputFeatures.string().c_str()); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 "ms, trustedOverlay=%s, hasToken=%s, " - "touchOcclusionMode=%s\n", + "touchOcclusionMode=%s, displayOrientation=%d\n", windowInfo->ownerPid, windowInfo->ownerUid, millis(windowInfo->dispatchingTimeout), toString(windowInfo->trustedOverlay), toString(windowInfo->token != nullptr), - toString(windowInfo->touchOcclusionMode).c_str()); + toString(windowInfo->touchOcclusionMode).c_str(), + windowInfo->displayOrientation); windowInfo->transform.dump(dump, "transform", INDENT4); } } else { @@ -5410,6 +5429,7 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { canceledWindows.c_str()); // Then clear the current touch state so we stop dispatching to them as well. + state.split = false; state.filterNonMonitors(); } return OK; @@ -5419,7 +5439,7 @@ void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool { // acquire lock std::scoped_lock _l(mLock); if (DEBUG_FOCUS) { - const sp<InputWindowHandle> windowHandle = getWindowHandleLocked(windowToken); + const sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(windowToken); ALOGI("Request to %s Pointer Capture from: %s.", enabled ? "enable" : "disable", windowHandle != nullptr ? windowHandle->getName().c_str() : "token without window"); @@ -5589,7 +5609,7 @@ void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window, +void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window, const std::string& reason) { const std::string windowLabel = getApplicationWindowLabel(nullptr, window); updateLastAnrStateLocked(windowLabel, reason); @@ -6257,4 +6277,31 @@ void InputDispatcher::displayRemoved(int32_t displayId) { mLooper->wake(); } +void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) { + // The listener sends the windows as a flattened array. Separate the windows by display for + // more convenient parsing. + std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay; + + for (const auto& info : windowInfos) { + handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>()); + handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info)); + } + setInputWindows(handlesPerDisplay); +} + +bool InputDispatcher::shouldDropInput( + const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const { + if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) || + (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) && + isWindowObscuredLocked(windowHandle))) { + ALOGW("Dropping %s event targeting %s as requested by input feature %s on display " + "%" PRId32 ".", + NamedEnum::string(entry.type).c_str(), windowHandle->getName().c_str(), + windowHandle->getInfo()->inputFeatures.string().c_str(), + windowHandle->getInfo()->displayId); + return true; + } + return false; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 30652c65ee..04913d479a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -37,10 +37,10 @@ #include <attestation/HmacKeyManager.h> #include <com/android/internal/compat/IPlatformCompatNative.h> +#include <gui/InputApplication.h> +#include <gui/WindowInfo.h> #include <input/Input.h> -#include <input/InputApplication.h> #include <input/InputTransport.h> -#include <input/InputWindow.h> #include <limits.h> #include <stddef.h> #include <ui/Region.h> @@ -58,6 +58,7 @@ #include <InputListener.h> #include <InputReporterInterface.h> +#include <gui/WindowInfosListener.h> namespace android::inputdispatcher { @@ -80,7 +81,7 @@ class Connection; * * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. */ -class InputDispatcher : public android::InputDispatcherInterface { +class InputDispatcher : public android::InputDispatcherInterface, public gui::WindowInfosListener { protected: ~InputDispatcher() override; @@ -109,8 +110,9 @@ public: std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override; - void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& - handlesPerDisplay) override; + void setInputWindows( + const std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>& + handlesPerDisplay) override; void setFocusedApplication( int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; @@ -127,7 +129,7 @@ public: base::Result<std::unique_ptr<InputChannel>> createInputChannel( const std::string& name) override; - void setFocusedWindow(const FocusRequest&) override; + void setFocusedWindow(const android::gui::FocusRequest&) override; base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, bool isGestureMonitor, const std::string& name, @@ -141,6 +143,8 @@ public: void displayRemoved(int32_t displayId) override; + void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override; + private: enum class DropReason { NOT_DROPPED, @@ -190,8 +194,8 @@ private: void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus, const std::string& reason) REQUIRES(mLock); // Enqueues a drag event. - void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting, - const MotionEntry& motionEntry) REQUIRES(mLock); + void enqueueDragEventLocked(const sp<android::gui::WindowInfoHandle>& windowToken, + bool isExiting, const MotionEntry& motionEntry) REQUIRES(mLock); // Adds an event to a queue of recent events for debugging purposes. void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock); @@ -208,11 +212,12 @@ private: // to transfer focus to a new application. std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock); - sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, - TouchState* touchState, - bool addOutsideTargets = false, - bool addPortalWindows = false, - bool ignoreDragWindow = false) REQUIRES(mLock); + sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, + int32_t y, TouchState* touchState, + bool addOutsideTargets = false, + bool addPortalWindows = false, + bool ignoreDragWindow = false) + REQUIRES(mLock); sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const REQUIRES(mLock); @@ -314,33 +319,36 @@ private: float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock); android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock); - std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay - GUARDED_BY(mLock); - void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles, - int32_t displayId) REQUIRES(mLock); + std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>> + mWindowHandlesByDisplay GUARDED_BY(mLock); + void setInputWindowsLocked( + const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles, + int32_t displayId) REQUIRES(mLock); // Get a reference to window handles by display, return an empty vector if not found. - const std::vector<sp<InputWindowHandle>>& getWindowHandlesLocked(int32_t displayId) const - REQUIRES(mLock); - sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const - REQUIRES(mLock); + const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked( + int32_t displayId) const REQUIRES(mLock); + sp<android::gui::WindowInfoHandle> getWindowHandleLocked( + const sp<IBinder>& windowHandleToken) const REQUIRES(mLock); // Same function as above, but faster. Since displayId is provided, this avoids the need // to loop through all displays. - sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken, - int displayId) const REQUIRES(mLock); - sp<InputWindowHandle> getWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const - REQUIRES(mLock); + sp<android::gui::WindowInfoHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken, + int displayId) const REQUIRES(mLock); + sp<android::gui::WindowInfoHandle> getWindowHandleLocked( + const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock); - sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock); - bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock); + sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const + REQUIRES(mLock); + bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const + REQUIRES(mLock); /* * Validate and update InputWindowHandles for a given display. */ void updateWindowHandlesForDisplayLocked( - const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) - REQUIRES(mLock); + const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles, + int32_t displayId) REQUIRES(mLock); std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock); std::unique_ptr<DragState> mDragState GUARDED_BY(mLock); @@ -473,7 +481,7 @@ private: AnrTracker mAnrTracker GUARDED_BY(mLock); // Contains the last window which received a hover event. - sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock); + sp<android::gui::WindowInfoHandle> mLastHoverWindowHandle GUARDED_BY(mLock); void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); @@ -492,20 +500,21 @@ private: nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock); std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const + int32_t displayId, + const std::vector<sp<android::gui::WindowInfoHandle>>& portalWindows) const REQUIRES(mLock); std::vector<TouchedMonitor> selectResponsiveMonitorsLocked( const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock); - void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, - BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) - REQUIRES(mLock); + void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, + int32_t targetFlags, BitSet32 pointerIds, + std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset, std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock); void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); - bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, + bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle, const InjectionState* injectionState); // Enqueue a drag event if needed, and update the touch state. // Uses findTouchedWindowTargetsLocked to make the decision @@ -520,15 +529,22 @@ private: std::vector<std::string> debugInfo; }; - TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle, - int32_t x, int32_t y) const REQUIRES(mLock); + TouchOcclusionInfo computeTouchOcclusionInfoLocked( + const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const + REQUIRES(mLock); bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock); - bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x, - int32_t y) const REQUIRES(mLock); - bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); - std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const; + bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, + int32_t x, int32_t y) const REQUIRES(mLock); + bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const + REQUIRES(mLock); + std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info, + bool isTouchWindow) const; std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle, - const sp<InputWindowHandle>& windowHandle); + const sp<android::gui::WindowInfoHandle>& windowHandle); + + bool shouldDropInput(const EventEntry& entry, + const sp<android::gui::WindowInfoHandle>& windowHandle) const + REQUIRES(mLock); // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work @@ -607,8 +623,8 @@ private: void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock); void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock); void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock); - void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason) - REQUIRES(mLock); + void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window, + const std::string& reason) REQUIRES(mLock); void updateLastAnrStateLocked(const InputApplicationHandle& application, const std::string& reason) REQUIRES(mLock); void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) @@ -643,6 +659,10 @@ private: void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + // Find touched state and touched window by token. + std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token) + REQUIRES(mLock); + // Statistics gathering. LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); LatencyTracker mLatencyTracker GUARDED_BY(mLock); @@ -652,6 +672,8 @@ private: sp<InputReporterInterface> mReporter; sp<com::android::internal::compat::IPlatformCompatNative> mCompatService; + + void onFirstRef() override; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 1c4980b302..7c463c8697 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -17,6 +17,7 @@ #ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H #define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H +#include <gui/constants.h> #include <input/InputTransport.h> #include <ui/Transform.h> #include <utils/BitSet.h> @@ -100,8 +101,11 @@ struct InputTarget { // (ignored for KeyEvents) float globalScaleFactor = 1.0f; + // Current display orientation + uint32_t displayOrientation = ui::Transform::ROT_0; + // Display-size in its natural rotation. Used for compatibility transform of raw coordinates. - int2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE}; + int2 displaySize = {INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE}; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 81b3cf025b..20b6eadf5b 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -14,13 +14,14 @@ * limitations under the License. */ -#include <input/InputWindow.h> +#include <gui/WindowInfo.h> #include "InputTarget.h" #include "TouchState.h" -using android::InputWindowHandle; +using android::gui::WindowInfo; +using android::gui::WindowInfoHandle; namespace android::inputdispatcher { @@ -51,7 +52,7 @@ void TouchState::copyFrom(const TouchState& other) { gestureMonitors = other.gestureMonitors; } -void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, +void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds) { if (targetFlags & InputTarget::FLAG_SPLIT) { split = true; @@ -76,7 +77,7 @@ void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, in windows.push_back(touchedWindow); } -void TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) { +void TouchState::addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle) { size_t numWindows = portalWindows.size(); for (size_t i = 0; i < numWindows; i++) { if (portalWindows[i] == windowHandle) { @@ -121,7 +122,7 @@ void TouchState::filterNonMonitors() { portalWindows.clear(); } -sp<InputWindowHandle> TouchState::getFirstForegroundWindowHandle() const { +sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const { for (size_t i = 0; i < windows.size(); i++) { const TouchedWindow& window = windows[i]; if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { @@ -137,7 +138,7 @@ bool TouchState::isSlippery() const { for (const TouchedWindow& window : windows) { if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { if (haveSlipperyForegroundWindow || - !window.windowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) { + !window.windowHandle->getInfo()->flags.test(WindowInfo::Flag::SLIPPERY)) { return false; } haveSlipperyForegroundWindow = true; diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 623c6a824f..a4e52b0d83 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -22,7 +22,9 @@ namespace android { -class InputWindowHandle; +namespace gui { +class WindowInfoHandle; +} namespace inputdispatcher { @@ -37,7 +39,7 @@ struct TouchState { // This collects the portal windows that the touch has gone through. Each portal window // targets a display (embedded display for most cases). With this info, we can add the // monitoring channels of the displays touched. - std::vector<sp<android::InputWindowHandle>> portalWindows; + std::vector<sp<android::gui::WindowInfoHandle>> portalWindows; std::vector<TouchedMonitor> gestureMonitors; @@ -45,14 +47,14 @@ struct TouchState { ~TouchState(); void reset(); void copyFrom(const TouchState& other); - void addOrUpdateWindow(const sp<android::InputWindowHandle>& windowHandle, int32_t targetFlags, - BitSet32 pointerIds); - void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle); + void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, + int32_t targetFlags, BitSet32 pointerIds); + void addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle); void addGestureMonitors(const std::vector<TouchedMonitor>& monitors); void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); void filterNonMonitors(); - sp<InputWindowHandle> getFirstForegroundWindowHandle() const; + sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const; bool isSlippery() const; }; diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index 8713aa3f56..4c31ec3acd 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -19,13 +19,15 @@ namespace android { -class InputWindowHandle; +namespace gui { +class WindowInfoHandle; +} namespace inputdispatcher { // Focus tracking for touch. struct TouchedWindow { - sp<android::InputWindowHandle> windowHandle; + sp<gui::WindowInfoHandle> windowHandle; int32_t targetFlags; BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set }; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 43428a0130..a7dccf0d19 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -19,18 +19,16 @@ #include <InputListener.h> #include <android-base/result.h> -#include <android/FocusRequest.h> +#include <android/gui/FocusRequest.h> #include <android/os/BlockUntrustedTouchesMode.h> -#include <android/os/ISetInputWindowsListener.h> #include <android/os/InputEventInjectionResult.h> #include <android/os/InputEventInjectionSync.h> -#include <input/InputApplication.h> +#include <gui/InputApplication.h> +#include <gui/WindowInfo.h> #include <input/InputDevice.h> #include <input/InputTransport.h> -#include <input/InputWindow.h> #include <unordered_map> - namespace android { /* Notifies the system about input events generated by the input reader. @@ -91,7 +89,7 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void setInputWindows( - const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& + const std::unordered_map<int32_t, std::vector<sp<gui::WindowInfoHandle>>>& handlesPerDisplay) = 0; /* Sets the focused application on the given display. @@ -162,7 +160,7 @@ public: /** * Sets focus on the specified window. */ - virtual void setFocusedWindow(const FocusRequest&) = 0; + virtual void setFocusedWindow(const gui::FocusRequest&) = 0; /** * Creates an input channel that may be used as targets for input events. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index fd591e0e1c..3c1e6370b7 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -20,8 +20,8 @@ #include "InputDispatcherConfiguration.h" #include <binder/IBinder.h> +#include <gui/InputApplication.h> #include <input/Input.h> -#include <input/InputApplication.h> #include <utils/RefBase.h> namespace android { diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index 8112038b16..3cf1b2b797 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -23,15 +23,14 @@ #include "InputHost.h" #include <android/os/BnInputFlinger.h> -#include <android/os/ISetInputWindowsListener.h> #include <binder/Binder.h> #include <cutils/compiler.h> #include <utils/String16.h> #include <utils/String8.h> #include <utils/StrongPointer.h> +using android::gui::FocusRequest; using android::os::BnInputFlinger; -using android::os::ISetInputWindowsListener; namespace android { @@ -44,10 +43,6 @@ public: InputFlinger() ANDROID_API; status_t dump(int fd, const Vector<String16>& args) override; - binder::Status setInputWindows(const std::vector<InputWindowInfo>&, - const sp<ISetInputWindowsListener>&) override { - return binder::Status::ok(); - } binder::Status createInputChannel(const std::string&, InputChannel*) override { return binder::Status::ok(); } diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 7db32e3071..ee7b392bcd 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -71,6 +71,7 @@ cc_defaults { "libstatslog", "libui", "libutils", + "InputFlingerProperties", ], static_libs: [ "libc++fs", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 68d5e7c300..166f358598 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -1252,12 +1252,11 @@ const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t devi bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) { - device->keyMap.keyCharacterMap->combine(*map); - device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName(); - return true; + if (device == nullptr || map == nullptr || device->keyMap.keyCharacterMap == nullptr) { + return false; } - return false; + device->keyMap.keyCharacterMap->combine(*map); + return true; } static std::string generateDescriptor(InputDeviceIdentifier& identifier) { diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 7af014cb34..1e9ec54d5d 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -18,7 +18,7 @@ #include "InputDevice.h" -#include <input/Flags.h> +#include <ftl/Flags.h> #include <algorithm> #include "CursorInputMapper.h" diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 16251ee0ca..9c8a29a059 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -19,8 +19,8 @@ #include "../Macros.h" +#include <ftl/NamedEnum.h> #include "PeripheralController.h" -#include "input/NamedEnum.h" // Log detailed debug messages about input device lights. static constexpr bool DEBUG_LIGHT_DETAILS = false; diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index d88b1bd704..3c3f88e36e 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -22,7 +22,7 @@ #include <unordered_map> #include <vector> -#include <input/Flags.h> +#include <ftl/Flags.h> #include <filesystem> #include <batteryservice/BatteryService.h> diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 2f2eba78b1..f32472d37b 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -17,8 +17,8 @@ #ifndef _UI_INPUTREADER_INPUT_DEVICE_H #define _UI_INPUTREADER_INPUT_DEVICE_H +#include <ftl/Flags.h> #include <input/DisplayViewport.h> -#include <input/Flags.h> #include <input/InputDevice.h> #include <input/PropertyMap.h> #include <stdint.h> diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index da0fea4616..7347b2cec4 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -17,7 +17,7 @@ #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H -#include <android-base/properties.h> +#include <InputFlingerProperties.sysprop.h> #include <input/DisplayViewport.h> #include <stdint.h> @@ -33,9 +33,7 @@ namespace android { // projection are part of the input window's transform. This means InputReader should work in the // un-rotated coordinate space. static bool isPerWindowInputRotationEnabled() { - static const bool PER_WINDOW_INPUT_ROTATION = - base::GetBoolProperty("persist.debug.per_window_input_rotation", false); - return PER_WINDOW_INPUT_ROTATION; + return sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false); } static int32_t getInverseRotation(int32_t orientation) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index ae89ca1ca9..ac5f6b652b 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -18,7 +18,7 @@ #include "../Macros.h" // clang-format on -#include <input/NamedEnum.h> +#include <ftl/NamedEnum.h> #include "TouchInputMapper.h" #include "CursorButtonAccumulator.h" @@ -470,6 +470,23 @@ void TouchInputMapper::configureParameters() { getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"), mParameters.orientationAware); + mParameters.orientation = Parameters::Orientation::ORIENTATION_0; + String8 orientationString; + if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"), + orientationString)) { + if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) { + ALOGW("The configuration 'touch.orientation' is only supported for touchscreens."); + } else if (orientationString == "ORIENTATION_90") { + mParameters.orientation = Parameters::Orientation::ORIENTATION_90; + } else if (orientationString == "ORIENTATION_180") { + mParameters.orientation = Parameters::Orientation::ORIENTATION_180; + } else if (orientationString == "ORIENTATION_270") { + mParameters.orientation = Parameters::Orientation::ORIENTATION_270; + } else if (orientationString != "ORIENTATION_0") { + ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string()); + } + } + mParameters.hasAssociatedDisplay = false; mParameters.associatedDisplayIsExternal = false; if (mParameters.orientationAware || @@ -508,6 +525,7 @@ void TouchInputMapper::dumpParameters(std::string& dump) { toString(mParameters.associatedDisplayIsExternal), mParameters.uniqueDisplayId.c_str()); dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); + dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n"; } void TouchInputMapper::configureRawPointerAxes() { @@ -669,7 +687,13 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { int32_t naturalPhysicalWidth, naturalPhysicalHeight; int32_t naturalPhysicalLeft, naturalPhysicalTop; int32_t naturalDeviceWidth, naturalDeviceHeight; - switch (mViewport.orientation) { + + // Apply the inverse of the input device orientation so that the surface is configured + // in the same orientation as the device. The input device orientation will be + // re-applied to mSurfaceOrientation. + const int32_t naturalSurfaceOrientation = + (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4; + switch (naturalSurfaceOrientation) { case DISPLAY_ORIENTATION_90: naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; @@ -752,6 +776,10 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; } + + // Apply the input device orientation for the device. + mSurfaceOrientation = + (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4; } else { mPhysicalWidth = rawWidth; mPhysicalHeight = rawHeight; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 920f8428f3..e104220e47 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -204,6 +204,15 @@ protected: bool hasAssociatedDisplay; bool associatedDisplayIsExternal; bool orientationAware; + + enum class Orientation : int32_t { + ORIENTATION_0 = DISPLAY_ORIENTATION_0, + ORIENTATION_90 = DISPLAY_ORIENTATION_90, + ORIENTATION_180 = DISPLAY_ORIENTATION_180, + ORIENTATION_270 = DISPLAY_ORIENTATION_270, + }; + Orientation orientation; + bool hasButtonUnderPad; std::string uniqueDisplayId; diff --git a/services/inputflinger/sysprop/Android.bp b/services/inputflinger/sysprop/Android.bp new file mode 100644 index 0000000000..b9d65ee246 --- /dev/null +++ b/services/inputflinger/sysprop/Android.bp @@ -0,0 +1,15 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +sysprop_library { + name: "InputFlingerProperties", + srcs: ["*.sysprop"], + api_packages: ["android.sysprop"], + property_owner: "Platform", +} diff --git a/services/inputflinger/sysprop/InputFlingerProperties.sysprop b/services/inputflinger/sysprop/InputFlingerProperties.sysprop new file mode 100644 index 0000000000..1c7e724332 --- /dev/null +++ b/services/inputflinger/sysprop/InputFlingerProperties.sysprop @@ -0,0 +1,27 @@ +# 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. + +module: "android.sysprop.InputFlingerProperties" +owner: Platform + +# When per-window-input-rotation is enabled, InputReader works in the un-rotated +# display coordinate space, and the display rotation is encoded as part of the +# input window transform that is sent from SurfaceFlinger to InputDispatcher. +prop { + api_name: "per_window_input_rotation" + type: Boolean + scope: Internal + access: ReadWrite + prop_name: "persist.debug.per_window_input_rotation" +} diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 918e1bef7a..e68692474d 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -52,6 +52,7 @@ cc_test { ], aidl: { include_dirs: [ + "frameworks/native/libs/gui", "frameworks/native/libs/input", ], }, diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp index 9051ff12c2..662be8063e 100644 --- a/services/inputflinger/tests/FocusResolver_test.cpp +++ b/services/inputflinger/tests/FocusResolver_test.cpp @@ -26,9 +26,12 @@ // atest inputflinger_tests:FocusResolverTest +using android::gui::FocusRequest; +using android::gui::WindowInfoHandle; + namespace android::inputdispatcher { -class FakeWindowHandle : public InputWindowHandle { +class FakeWindowHandle : public WindowInfoHandle { public: FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable, bool visible) { @@ -38,7 +41,6 @@ public: mInfo.focusable = focusable; } - bool updateInfo() { return true; } void setFocusable(bool focusable) { mInfo.focusable = focusable; } void setVisible(bool visible) { mInfo.visible = visible; } }; @@ -47,7 +49,7 @@ TEST(FocusResolverTest, SetFocusedWindow) { sp<IBinder> focusableWindowToken = new BBinder(); sp<IBinder> invisibleWindowToken = new BBinder(); sp<IBinder> unfocusableWindowToken = new BBinder(); - std::vector<sp<InputWindowHandle>> windows; + std::vector<sp<WindowInfoHandle>> windows; windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */, true /* visible */)); windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */, @@ -82,7 +84,7 @@ TEST(FocusResolverTest, SetFocusedMirroredWindow) { sp<IBinder> focusableWindowToken = new BBinder(); sp<IBinder> invisibleWindowToken = new BBinder(); sp<IBinder> unfocusableWindowToken = new BBinder(); - std::vector<sp<InputWindowHandle>> windows; + std::vector<sp<WindowInfoHandle>> windows; windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */, true /* visible */)); windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */, @@ -120,7 +122,7 @@ TEST(FocusResolverTest, SetFocusedMirroredWindow) { TEST(FocusResolverTest, SetInputWindows) { sp<IBinder> focusableWindowToken = new BBinder(); - std::vector<sp<InputWindowHandle>> windows; + std::vector<sp<WindowInfoHandle>> windows; sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */, true /* visible */); windows.push_back(window); @@ -142,7 +144,7 @@ TEST(FocusResolverTest, SetInputWindows) { TEST(FocusResolverTest, FocusRequestsCanBePending) { sp<IBinder> invisibleWindowToken = new BBinder(); - std::vector<sp<InputWindowHandle>> windows; + std::vector<sp<WindowInfoHandle>> windows; sp<FakeWindowHandle> invisibleWindow = new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */, @@ -166,7 +168,7 @@ TEST(FocusResolverTest, FocusRequestsCanBePending) { TEST(FocusResolverTest, FocusRequestsArePersistent) { sp<IBinder> windowToken = new BBinder(); - std::vector<sp<InputWindowHandle>> windows; + std::vector<sp<WindowInfoHandle>> windows; sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken, false /* focusable */, true /* visible */); @@ -207,7 +209,7 @@ TEST(FocusResolverTest, FocusRequestsArePersistent) { TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) { sp<IBinder> hostWindowToken = new BBinder(); - std::vector<sp<InputWindowHandle>> windows; + std::vector<sp<WindowInfoHandle>> windows; sp<FakeWindowHandle> hostWindow = new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */, @@ -258,7 +260,7 @@ TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) { } TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) { sp<IBinder> windowToken = new BBinder(); - std::vector<sp<InputWindowHandle>> windows; + std::vector<sp<WindowInfoHandle>> windows; sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken, true /* focusable */, true /* visible */); diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl index 5c8a8da612..5aeb21f6b4 100644 --- a/services/inputflinger/tests/IInputFlingerQuery.aidl +++ b/services/inputflinger/tests/IInputFlingerQuery.aidl @@ -14,17 +14,14 @@ * limitations under the License. */ -import android.FocusRequest; import android.InputChannel; -import android.InputWindowInfo; -import android.os.ISetInputWindowsListener; +import android.gui.FocusRequest; +import android.gui.WindowInfo; /** @hide */ interface IInputFlingerQuery { /* Test interfaces */ - void getInputWindows(out InputWindowInfo[] inputHandles); void getInputChannels(out InputChannel[] channels); - void getLastFocusRequest(out FocusRequest request); void resetInputManager(); } diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp index c0ada9d517..f626d56b8c 100644 --- a/services/inputflinger/tests/InputClassifierConverter_test.cpp +++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp @@ -17,9 +17,9 @@ #include "../InputClassifierConverter.h" #include <gtest/gtest.h> +#include <gui/constants.h> #include <utils/BitSet.h> - using namespace android::hardware::input; namespace android { diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index a72df01ce6..3a9994eed9 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -16,6 +16,7 @@ #include "../InputClassifier.h" #include <gtest/gtest.h> +#include <gui/constants.h> #include "TestInputListener.h" diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 7d4c6386f8..ee4d3b8e79 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -29,9 +29,12 @@ #include <vector> using android::base::StringPrintf; +using android::gui::FocusRequest; +using android::gui::TouchOcclusionMode; +using android::gui::WindowInfo; +using android::gui::WindowInfoHandle; using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; -using android::os::TouchOcclusionMode; using namespace android::flag_operators; namespace android::inputdispatcher { @@ -43,7 +46,8 @@ static const nsecs_t ARBITRARY_TIME = 1234; static const int32_t DEVICE_ID = 1; // An arbitrary display id. -static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; +static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; +static constexpr int32_t SECOND_DISPLAY_ID = 1; // An arbitrary injector pid / uid pair that has permission to inject events. static const int32_t INJECTOR_PID = 999; @@ -473,8 +477,8 @@ protected: } } - void setFocusedWindow(const sp<InputWindowHandle>& window, - const sp<InputWindowHandle>& focusedWindow = nullptr) { + void setFocusedWindow(const sp<WindowInfoHandle>& window, + const sp<WindowInfoHandle>& focusedWindow = nullptr) { FocusRequest request; request.token = window->getToken(); request.windowName = window->getName(); @@ -531,8 +535,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -545,9 +549,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, - pointerCoords); + ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, InputEventInjectionSync::NONE, 0ms, 0)) @@ -558,9 +562,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, - pointerCoords); + ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, InputEventInjectionSync::NONE, 0ms, 0)) @@ -572,9 +576,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, - pointerCoords); + ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, InputEventInjectionSync::NONE, 0ms, 0)) @@ -585,9 +589,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, - pointerCoords); + ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, InputEventInjectionSync::NONE, 0ms, 0)) @@ -597,8 +601,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -608,8 +612,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -621,8 +625,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -633,8 +637,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -647,8 +651,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -896,7 +900,7 @@ protected: std::string mName; }; -class FakeWindowHandle : public InputWindowHandle { +class FakeWindowHandle : public WindowInfoHandle { public: static const int32_t WIDTH = 600; static const int32_t HEIGHT = 800; @@ -918,7 +922,7 @@ public: mInfo.token = *token; mInfo.id = sId++; mInfo.name = name; - mInfo.type = InputWindowInfo::Type::APPLICATION; + mInfo.type = WindowInfo::Type::APPLICATION; mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; mInfo.alpha = 1.0; mInfo.frameLeft = 0; @@ -938,7 +942,14 @@ public: mInfo.displayId = displayId; } - virtual bool updateInfo() { return true; } + sp<FakeWindowHandle> clone( + const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, + const sp<InputDispatcher>& dispatcher, int32_t displayId) { + sp<FakeWindowHandle> handle = + new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)", + displayId, mInfo.token); + return handle; + } void setFocusable(bool focusable) { mInfo.focusable = focusable; } @@ -952,9 +963,7 @@ public: void setAlpha(float alpha) { mInfo.alpha = alpha; } - void setTouchOcclusionMode(android::os::TouchOcclusionMode mode) { - mInfo.touchOcclusionMode = mode; - } + void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; } void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; } @@ -968,11 +977,11 @@ public: mInfo.addTouchableRegion(frame); } - void addFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags |= flags; } + void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; } - void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; } + void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; } - void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; } + void setInputFeatures(WindowInfo::Feature features) { mInfo.inputFeatures = features; } void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) { mInfo.transform.set(dsdx, dtdx, dtdy, dsdy); @@ -1106,7 +1115,7 @@ public: void assertNoEvents() { if (mInputReceiver == nullptr && - mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) { + mInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) { return; // Can't receive events if the window does not have input channel } ASSERT_NE(nullptr, mInputReceiver) @@ -1271,8 +1280,9 @@ public: mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE, mButtonState, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition, - mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime, - mPointers.size(), pointerProperties.data(), pointerCoords.data()); + mRawYCursorPosition, mDisplayOrientation, mDisplayWidth, mDisplayHeight, + mEventTime, mEventTime, mPointers.size(), pointerProperties.data(), + pointerCoords.data()); return event; } @@ -1287,8 +1297,9 @@ private: int32_t mFlags{0}; float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; - int32_t mDisplayWidth{AMOTION_EVENT_INVALID_DISPLAY_SIZE}; - int32_t mDisplayHeight{AMOTION_EVENT_INVALID_DISPLAY_SIZE}; + uint32_t mDisplayOrientation{ui::Transform::ROT_0}; + int32_t mDisplayWidth{INVALID_DISPLAY_SIZE}; + int32_t mDisplayHeight{INVALID_DISPLAY_SIZE}; std::vector<PointerBuilder> mPointers; }; @@ -1415,7 +1426,7 @@ TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) { sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 100, 100)); - window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1438,7 +1449,7 @@ TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) { sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 100, 100)); - window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -1474,11 +1485,11 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { sp<FakeWindowHandle> windowLeft = new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); windowLeft->setFrame(Rect(0, 0, 600, 800)); - windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + windowLeft->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); sp<FakeWindowHandle> windowRight = new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); windowRight->setFrame(Rect(600, 0, 1200, 800)); - windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + windowRight->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -1585,7 +1596,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 1200, 800)); - window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -1667,11 +1678,11 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { sp<FakeWindowHandle> windowLeft = new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); windowLeft->setFrame(Rect(0, 0, 600, 800)); - windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + windowLeft->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); sp<FakeWindowHandle> windowRight = new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); windowRight->setFrame(Rect(600, 0, 1200, 800)); - windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + windowRight->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -1865,15 +1876,13 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 600, 400)); - firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); // Create a non touch modal window that supports split touch sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(0, 400, 600, 800)); - secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); // Add the windows to the dispatcher mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); @@ -1939,15 +1948,13 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 600, 400)); - firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); // Create a non touch modal window that supports split touch sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(0, 400, 600, 800)); - secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); // Add the windows to the dispatcher mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); @@ -2004,6 +2011,134 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { secondWindow->assertNoEvents(); } +// This case will create two windows and one mirrored window on the default display and mirror +// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put +// the windows info of second display before default display. +TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> firstWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); + firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + sp<FakeWindowHandle> secondWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> mirrorWindowInPrimary = + firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT); + mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200)); + mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> firstWindowInSecondary = + firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> secondWindowInSecondary = + secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + // Update window info, let it find window handle of second display first. + mDispatcher->setInputWindows( + {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}}, + {ADISPLAY_ID_DEFAULT, + {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Window should receive motion event. + firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + // Transfer touch focus + ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(), + secondWindowInPrimary->getToken())); + // The first window gets cancel. + firstWindowInPrimary->consumeMotionCancel(); + secondWindowInPrimary->consumeMotionDown(); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionMove(); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionUp(); +} + +// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use +// 'transferTouch' api. +TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> firstWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); + firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + sp<FakeWindowHandle> secondWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> mirrorWindowInPrimary = + firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT); + mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200)); + mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> firstWindowInSecondary = + firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> secondWindowInSecondary = + secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + // Update window info, let it find window handle of second display first. + mDispatcher->setInputWindows( + {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}}, + {ADISPLAY_ID_DEFAULT, + {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}}); + + // Touch on second display. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Window should receive motion event. + firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + + // Transfer touch focus + ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken())); + + // The first window gets cancel. + firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID); + secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + SECOND_DISPLAY_ID, {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID); +} + TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = @@ -2065,15 +2200,13 @@ TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) { sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 600, 400)); - firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); // Create second non touch modal window that supports split touch sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(0, 400, 600, 800)); - secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); // Add the windows to the dispatcher mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); @@ -2168,6 +2301,18 @@ public: expectedDisplayId, expectedFlags); } + void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + expectedDisplayId, expectedFlags); + } + + void consumeMotionPointerDown(int32_t pointerIdx) { + int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | + (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, ADISPLAY_ID_DEFAULT, + 0 /*expectedFlags*/); + } + MotionEvent* consumeMotion() { InputEvent* event = mInputReceiver->consume(); if (!event) { @@ -2314,6 +2459,91 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { 0 /*expectedFlags*/); } +TEST_F(InputDispatcherTest, GestureMonitor_SplitIfNoWindowTouched) { + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Second finger down on window, the window should receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionPointerDown(1 /* pointerIndex */); +} + +TEST_F(InputDispatcherTest, GestureMonitor_NoSplitAfterPilfer) { + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Gesture monitor pilfer the pointers. + mDispatcher->pilferPointers(monitor.getToken()); + + // Second finger down on window, the window should not receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->assertNoEvents(); + monitor.consumeMotionPointerDown(1 /* pointerIndex */); +} + /** * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to * the device default right away. In the test scenario, we check both the default value, @@ -2437,7 +2667,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState); } -TEST_F(InputDispatcherTest, NonPointerMotionEvent_NotTransformed) { +TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); @@ -2457,20 +2687,19 @@ TEST_F(InputDispatcherTest, NonPointerMotionEvent_NotTransformed) { // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked. window->consumeFocusEvent(true); - constexpr const std::array nonPointerSources = {AINPUT_SOURCE_TRACKBALL, - AINPUT_SOURCE_MOUSE_RELATIVE, - AINPUT_SOURCE_JOYSTICK}; - for (const int source : nonPointerSources) { - // Notify motion with a non-pointer source. - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, source, ADISPLAY_ID_DEFAULT); + constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD, + AMOTION_EVENT_ACTION_DOWN), + std::pair(AINPUT_SOURCE_JOYSTICK, + AMOTION_EVENT_ACTION_MOVE)}; + for (const auto& [source, action] : nonTransformedSources) { + const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT); mDispatcher->notifyMotion(&motionArgs); MotionEvent* event = window->consumeMotion(); ASSERT_NE(event, nullptr); const MotionEvent& motionEvent = *event; - EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, motionEvent.getAction()); + EXPECT_EQ(action, motionEvent.getAction()); EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount()); float expectedX = motionArgs.pointerCoords[0].getX(); @@ -2742,8 +2971,7 @@ TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) { sp<FakeWindowHandle> slipperyExitWindow = new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); - slipperyExitWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SLIPPERY); + slipperyExitWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SLIPPERY); // Make sure this one overlaps the bottom window slipperyExitWindow->setFrame(Rect(25, 25, 75, 75)); // Change the owner uid/pid of the window so that it is considered to be occluding the bottom @@ -2928,7 +3156,6 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEvent /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: - static constexpr int32_t SECOND_DISPLAY_ID = 1; virtual void SetUp() override { InputDispatcherTest::SetUp(); @@ -3091,8 +3318,6 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) class InputFilterTest : public InputDispatcherTest { protected: - static constexpr int32_t SECOND_DISPLAY_ID = 1; - void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) { NotifyMotionArgs motionArgs; @@ -3226,9 +3451,9 @@ protected: DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - 0 /*AMOTION_EVENT_INVALID_DISPLAY_SIZE*/, - 0 /*AMOTION_EVENT_INVALID_DISPLAY_SIZE*/, eventTime, eventTime, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, + 0 /*INVALID_DISPLAY_SIZE*/, 0 /*INVALID_DISPLAY_SIZE*/, eventTime, + eventTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER; @@ -3286,12 +3511,12 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30)); // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this // window. - mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + mUnfocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mFocusedWindow = new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); - mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + mFocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -3400,14 +3625,12 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { ADISPLAY_ID_DEFAULT); // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window. // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. - mWindow1->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + mWindow1->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); mWindow1->setFrame(Rect(0, 0, 100, 100)); mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", ADISPLAY_ID_DEFAULT, mWindow1->getToken()); - mWindow2->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + mWindow2->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}}); @@ -3418,7 +3641,7 @@ protected: sp<FakeWindowHandle> mWindow2; // Helper function to convert the point from screen coordinates into the window's space - static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) { + static PointF getPointInWindow(const WindowInfo* windowInfo, const PointF& point) { vec2 vals = windowInfo->transform.transform(point.x, point.y); return {vals.x, vals.y}; } @@ -3606,7 +3829,7 @@ class InputDispatcherSingleWindowAnr : public InputDispatcherTest { mWindow->setFocusable(true); // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this // window. - mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + mWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); @@ -4007,16 +4230,15 @@ class InputDispatcherMultiWindowAnr : public InputDispatcherTest { // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this // window. // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped - mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH | - InputWindowInfo::Flag::SPLIT_TOUCH); + mUnfocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | + WindowInfo::Flag::WATCH_OUTSIDE_TOUCH | + WindowInfo::Flag::SPLIT_TOUCH); mFocusedWindow = new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT); mFocusedWindow->setDispatchingTimeout(30ms); mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); - mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | - InputWindowInfo::Flag::SPLIT_TOUCH); + mFocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); @@ -4384,7 +4606,7 @@ class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest { "Window without input channel", ADISPLAY_ID_DEFAULT, std::make_optional<sp<IBinder>>(nullptr) /*token*/); - mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); // It's perfectly valid for this window to not have an associated input channel @@ -4426,7 +4648,7 @@ TEST_F(InputDispatcherMultiWindowOcclusionTests, "Window with input channel and NO_INPUT_CHANNEL", ADISPLAY_ID_DEFAULT); - mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}}); @@ -4747,10 +4969,10 @@ protected: mTouchWindow.clear(); } - sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name, - os::TouchOcclusionMode mode, float alpha = 1.0f) { + sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name, TouchOcclusionMode mode, + float alpha = 1.0f) { sp<FakeWindowHandle> window = getWindow(uid, name); - window->setFlags(InputWindowInfo::Flag::NOT_TOUCHABLE); + window->setFlags(WindowInfo::Flag::NOT_TOUCHABLE); window->setTouchOcclusionMode(mode); window->setAlpha(alpha); return window; @@ -4864,7 +5086,7 @@ TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacityAndWatchOutside_ReceivesOutsideEvent) { const sp<FakeWindowHandle>& w = getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f); - w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH); + w->addFlags(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); touch(); @@ -4875,7 +5097,7 @@ TEST_F(InputDispatcherUntrustedTouchesTest, TEST_F(InputDispatcherUntrustedTouchesTest, OutsideEvent_HasZeroCoordinates) { const sp<FakeWindowHandle>& w = getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f); - w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH); + w->addFlags(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); touch(); @@ -5129,11 +5351,11 @@ protected: mApp = std::make_shared<FakeApplicationHandle>(); mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mWindow->setFrame(Rect(0, 0, 100, 100)); - mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + mWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); mSecondWindow->setFrame(Rect(100, 0, 200, 100)); - mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + mSecondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}}); @@ -5341,4 +5563,134 @@ TEST_F(InputDispatcherDragTests, DragAndDrop_InvalidWindow) { mSecondWindow->assertNoEvents(); } +class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {}; + +TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + window->setInputFeatures(WindowInfo::Feature::DROP_INPUT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + // With the flag set, window should not get any input + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->assertNoEvents(); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->assertNoEvents(); + + // With the flag cleared, the window should get input + window->setInputFeatures({}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); +} + +TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) { + std::shared_ptr<FakeApplicationHandle> obscuringApplication = + std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> obscuringWindow = + new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow", + ADISPLAY_ID_DEFAULT); + obscuringWindow->setFrame(Rect(0, 0, 50, 50)); + obscuringWindow->setOwnerInfo(111, 111); + obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED); + window->setOwnerInfo(222, 222); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + // With the flag set, window should not get any input + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->assertNoEvents(); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->assertNoEvents(); + + // With the flag cleared, the window should get input + window->setInputFeatures({}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}}); + + keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + window->assertNoEvents(); +} + +TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) { + std::shared_ptr<FakeApplicationHandle> obscuringApplication = + std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> obscuringWindow = + new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow", + ADISPLAY_ID_DEFAULT); + obscuringWindow->setFrame(Rect(0, 0, 50, 50)); + obscuringWindow->setOwnerInfo(111, 111); + obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED); + window->setOwnerInfo(222, 222); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + // With the flag set, window should not get any input + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->assertNoEvents(); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->assertNoEvents(); + + // When the window is no longer obscured because it went on top, it should get input + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}}); + + keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp index c368e79f41..454e531af6 100644 --- a/services/inputflinger/tests/InputFlingerService_test.cpp +++ b/services/inputflinger/tests/InputFlingerService_test.cpp @@ -18,9 +18,7 @@ #include <IInputFlingerQuery.h> #include <android/os/BnInputFlinger.h> -#include <android/os/BnSetInputWindowsListener.h> #include <android/os/IInputFlinger.h> -#include <android/os/ISetInputWindowsListener.h> #include <binder/Binder.h> #include <binder/IPCThreadState.h> @@ -30,7 +28,6 @@ #include <input/Input.h> #include <input/InputTransport.h> -#include <input/InputWindow.h> #include <gtest/gtest.h> #include <inttypes.h> @@ -44,56 +41,18 @@ #define TAG "InputFlingerServiceTest" +using android::gui::FocusRequest; using android::os::BnInputFlinger; -using android::os::BnSetInputWindowsListener; using android::os::IInputFlinger; -using android::os::ISetInputWindowsListener; using std::chrono_literals::operator""ms; using std::chrono_literals::operator""s; namespace android { -static const sp<IBinder> TestInfoToken = new BBinder(); -static const sp<IBinder> FocusedTestInfoToken = new BBinder(); -static constexpr int32_t TestInfoId = 1; -static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo"; -static constexpr Flags<InputWindowInfo::Flag> TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE; -static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD; -static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms; -static constexpr int32_t TestInfoFrameLeft = 93; -static constexpr int32_t TestInfoFrameTop = 34; -static constexpr int32_t TestInfoFrameRight = 16; -static constexpr int32_t TestInfoFrameBottom = 19; -static constexpr int32_t TestInfoSurfaceInset = 17; -static constexpr float TestInfoGlobalScaleFactor = 0.3; -static constexpr float TestInfoWindowXScale = 0.4; -static constexpr float TestInfoWindowYScale = 0.5; -static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */, - 450 /* bottom */}; -static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect); -static constexpr bool TestInfoVisible = false; -static constexpr bool TestInfoTrustedOverlay = true; -static constexpr bool TestInfoFocusable = false; -static constexpr bool TestInfoHasWallpaper = false; -static constexpr bool TestInfoPaused = false; -static constexpr int32_t TestInfoOwnerPid = 19; -static constexpr int32_t TestInfoOwnerUid = 24; -static constexpr InputWindowInfo::Feature TestInfoInputFeatures = - InputWindowInfo::Feature::NO_INPUT_CHANNEL; -static constexpr int32_t TestInfoDisplayId = 34; -static constexpr int32_t TestInfoPortalToDisplayId = 2; -static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true; -static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder(); - -static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo"; -static const sp<IBinder> TestAppInfoToken = new BBinder(); -static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms; - static const String16 kTestServiceName = String16("InputFlingerService"); static const String16 kQueryServiceName = String16("InputFlingerQueryService"); -struct SetInputWindowsListener; // --- InputFlingerServiceTest --- class InputFlingerServiceTest : public testing::Test { public: @@ -102,32 +61,15 @@ public: protected: void InitializeInputFlinger(); - void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos); - void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken, - nsecs_t timestampNanos); - - void setInputWindowsFinished(); - void verifyInputWindowInfo(const InputWindowInfo& info) const; - InputWindowInfo& getInfo() const { return const_cast<InputWindowInfo&>(mInfo); } sp<IInputFlinger> mService; sp<IInputFlingerQuery> mQuery; private: - sp<SetInputWindowsListener> mSetInputWindowsListener; std::unique_ptr<InputChannel> mServerChannel, mClientChannel; - InputWindowInfo mInfo; std::mutex mLock; - std::condition_variable mSetInputWindowsFinishedCondition; }; -struct SetInputWindowsListener : BnSetInputWindowsListener { - explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {} - - binder::Status onSetInputWindowsFinished() override; - - std::function<void()> mCbFunc; -}; class TestInputManager : public BnInputFlinger { protected: @@ -136,16 +78,10 @@ protected: public: TestInputManager(){}; - binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles); binder::Status getInputChannels(std::vector<::android::InputChannel>* channels); - binder::Status getLastFocusRequest(FocusRequest*); status_t dump(int fd, const Vector<String16>& args) override; - binder::Status setInputWindows( - const std::vector<InputWindowInfo>& handles, - const sp<ISetInputWindowsListener>& setInputWindowsListener) override; - binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; binder::Status setFocusedWindow(const FocusRequest&) override; @@ -154,63 +90,28 @@ public: private: mutable Mutex mLock; - std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay; std::vector<std::shared_ptr<InputChannel>> mInputChannels; - FocusRequest mFocusRequest; }; class TestInputQuery : public BnInputFlingerQuery { public: TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){}; - binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override; binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override; - binder::Status getLastFocusRequest(FocusRequest*) override; binder::Status resetInputManager() override; private: sp<android::TestInputManager> mManager; }; -binder::Status TestInputQuery::getInputWindows( - std::vector<::android::InputWindowInfo>* inputHandles) { - return mManager->getInputWindows(inputHandles); -} - binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) { return mManager->getInputChannels(channels); } -binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) { - return mManager->getLastFocusRequest(request); -} - binder::Status TestInputQuery::resetInputManager() { mManager->reset(); return binder::Status::ok(); } -binder::Status SetInputWindowsListener::onSetInputWindowsFinished() { - if (mCbFunc != nullptr) { - mCbFunc(); - } - return binder::Status::ok(); -} - -binder::Status TestInputManager::setInputWindows( - const std::vector<InputWindowInfo>& infos, - const sp<ISetInputWindowsListener>& setInputWindowsListener) { - AutoMutex _l(mLock); - - for (const auto& info : infos) { - mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>()); - mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info)); - } - if (setInputWindowsListener) { - setInputWindowsListener->onSetInputWindowsFinished(); - } - return binder::Status::ok(); -} - binder::Status TestInputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { AutoMutex _l(mLock); @@ -248,16 +149,6 @@ status_t TestInputManager::dump(int fd, const Vector<String16>& args) { return NO_ERROR; } -binder::Status TestInputManager::getInputWindows( - std::vector<::android::InputWindowInfo>* inputInfos) { - for (auto& [displayId, inputHandles] : mHandlesPerDisplay) { - for (auto& inputHandle : inputHandles) { - inputInfos->push_back(*inputHandle->getInfo()); - } - } - return binder::Status::ok(); -} - binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) { channels->clear(); for (std::shared_ptr<InputChannel>& channel : mInputChannels) { @@ -266,64 +157,16 @@ binder::Status TestInputManager::getInputChannels(std::vector<::android::InputCh return binder::Status::ok(); } -binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) { - *request = mFocusRequest; - return binder::Status::ok(); -} - binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) { - mFocusRequest = request; return binder::Status::ok(); } void TestInputManager::reset() { - mHandlesPerDisplay.clear(); mInputChannels.clear(); - mFocusRequest = FocusRequest(); } void InputFlingerServiceTest::SetUp() { - mSetInputWindowsListener = new SetInputWindowsListener([&]() { - std::unique_lock<std::mutex> lock(mLock); - mSetInputWindowsFinishedCondition.notify_all(); - }); InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); - - mInfo.token = TestInfoToken; - mInfo.id = TestInfoId; - mInfo.name = TestInfoName; - mInfo.flags = TestInfoFlags; - mInfo.type = TestInfoType; - mInfo.dispatchingTimeout = TestInfoDispatchingTimeout; - mInfo.frameLeft = TestInfoFrameLeft; - mInfo.frameTop = TestInfoFrameTop; - mInfo.frameRight = TestInfoFrameRight; - mInfo.frameBottom = TestInfoFrameBottom; - mInfo.surfaceInset = TestInfoSurfaceInset; - mInfo.globalScaleFactor = TestInfoGlobalScaleFactor; - mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale, - TestInfoFrameTop, 0, 0, 1}); - mInfo.touchableRegion = TestInfoTouchableRegion; - mInfo.visible = TestInfoVisible; - mInfo.trustedOverlay = TestInfoTrustedOverlay; - mInfo.focusable = TestInfoFocusable; - - mInfo.hasWallpaper = TestInfoHasWallpaper; - mInfo.paused = TestInfoPaused; - mInfo.ownerPid = TestInfoOwnerPid; - mInfo.ownerUid = TestInfoOwnerUid; - mInfo.inputFeatures = TestInfoInputFeatures; - mInfo.displayId = TestInfoDisplayId; - mInfo.portalToDisplayId = TestInfoPortalToDisplayId; - mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop; - mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle; - - mInfo.applicationInfo.name = TestAppInfoName; - mInfo.applicationInfo.token = TestAppInfoToken; - mInfo.applicationInfo.dispatchingTimeoutMillis = - std::chrono::duration_cast<std::chrono::milliseconds>(TestAppInfoDispatchingTimeout) - .count(); - InitializeInputFlinger(); } @@ -331,10 +174,6 @@ void InputFlingerServiceTest::TearDown() { mQuery->resetInputManager(); } -void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const { - EXPECT_EQ(mInfo, info); -} - void InputFlingerServiceTest::InitializeInputFlinger() { sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName)); ASSERT_TRUE(input != nullptr); @@ -345,40 +184,6 @@ void InputFlingerServiceTest::InitializeInputFlinger() { mQuery = interface_cast<IInputFlingerQuery>(input); } -void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) { - std::unique_lock<std::mutex> lock(mLock); - mService->setInputWindows(infos, mSetInputWindowsListener); - // Verify listener call - EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout); -} - -void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token, - const sp<IBinder> focusedToken, - nsecs_t timestampNanos) { - FocusRequest request; - request.token = TestInfoToken; - request.focusedToken = focusedToken; - request.timestamp = timestampNanos; - mService->setFocusedWindow(request); - // call set input windows and wait for the callback to drain the queue. - setInputWindowsByInfos(std::vector<InputWindowInfo>()); -} - -/** - * Test InputFlinger service interface SetInputWindows - */ -TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) { - std::vector<InputWindowInfo> infos = {getInfo()}; - setInputWindowsByInfos(infos); - - // Verify input windows from service - std::vector<::android::InputWindowInfo> windowInfos; - mQuery->getInputWindows(&windowInfos); - for (const ::android::InputWindowInfo& windowInfo : windowInfos) { - verifyInputWindowInfo(windowInfo); - } -} - /** * Test InputFlinger service interface createInputChannel */ @@ -397,7 +202,7 @@ TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) { EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK); } -TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) { +TEST_F(InputFlingerServiceTest, CreateInputChannel) { InputChannel channel; ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk()); @@ -411,30 +216,6 @@ TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) { EXPECT_EQ(channels.size(), 0UL); } -TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now); - - FocusRequest request; - mQuery->getLastFocusRequest(&request); - - EXPECT_EQ(request.token, TestInfoToken); - EXPECT_EQ(request.focusedToken, nullptr); - EXPECT_EQ(request.timestamp, now); -} - -TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now); - - FocusRequest request; - mQuery->getLastFocusRequest(&request); - - EXPECT_EQ(request.token, TestInfoToken); - EXPECT_EQ(request.focusedToken, FocusedTestInfoToken); - EXPECT_EQ(request.timestamp, now); -} - } // namespace android int main(int argc, char** argv) { diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 38dfe4041f..b419d9ab3d 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -16,6 +16,7 @@ #include <CursorInputMapper.h> #include <InputDevice.h> +#include <InputFlingerProperties.sysprop.h> #include <InputMapper.h> #include <InputReader.h> #include <InputReaderBase.h> @@ -32,6 +33,7 @@ #include <VibratorInputMapper.h> #include <android-base/thread_annotations.h> #include <gtest/gtest.h> +#include <gui/constants.h> #include <inttypes.h> #include <math.h> @@ -94,6 +96,17 @@ const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightC {"green", LightColor::GREEN}, {"blue", LightColor::BLUE}}; +static int32_t getInverseRotation(int32_t orientation) { + switch (orientation) { + case DISPLAY_ORIENTATION_90: + return DISPLAY_ORIENTATION_270; + case DISPLAY_ORIENTATION_270: + return DISPLAY_ORIENTATION_90; + default: + return orientation; + } +} + // --- FakePointerController --- class FakePointerController : public PointerControllerInterface { @@ -2662,6 +2675,7 @@ protected: static const int32_t DEVICE_CONTROLLER_NUMBER; static const Flags<InputDeviceClass> DEVICE_CLASSES; static const int32_t EVENTHUB_ID; + static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE; std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; @@ -2678,11 +2692,19 @@ protected: mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } - void SetUp() override { SetUp(DEVICE_CLASSES); } + void SetUp() override { + // Ensure per_window_input_rotation is enabled. + sysprop::InputFlingerProperties::per_window_input_rotation(true); + + SetUp(DEVICE_CLASSES); + } void TearDown() override { mFakeListener.clear(); mFakePolicy.clear(); + + sysprop::InputFlingerProperties::per_window_input_rotation( + INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE); } void addConfigurationProperty(const char* key, const char* value) { @@ -2794,6 +2816,8 @@ const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0; const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES = Flags<InputDeviceClass>(0); // not needed for current tests const int32_t InputMapperTest::EVENTHUB_ID = 1; +const std::optional<bool> InputMapperTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE = + sysprop::InputFlingerProperties::per_window_input_rotation(); // --- SwitchInputMapperTest --- @@ -4090,8 +4114,11 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); } -TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) { +TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) { addConfigurationProperty("cursor.mode", "navigation"); + // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not + // need to be rotated. + addConfigurationProperty("cursor.orientationAware", "1"); CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); prepareDisplay(DISPLAY_ORIENTATION_90); @@ -4105,9 +4132,10 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMot ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); } -TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) { +TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) { addConfigurationProperty("cursor.mode", "navigation"); - addConfigurationProperty("cursor.orientationAware", "1"); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); prepareDisplay(DISPLAY_ORIENTATION_0); @@ -4121,14 +4149,14 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); prepareDisplay(DISPLAY_ORIENTATION_90); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); prepareDisplay(DISPLAY_ORIENTATION_180); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); @@ -4141,14 +4169,14 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); prepareDisplay(DISPLAY_ORIENTATION_270); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); } TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { @@ -4634,6 +4662,8 @@ protected: void prepareLocationCalibration(); int32_t toRawX(float displayX); int32_t toRawY(float displayY); + int32_t toRotatedRawX(float displayX); + int32_t toRotatedRawY(float displayY); float toCookedX(float rawX, float rawY); float toCookedY(float rawX, float rawY); float toDisplayX(int32_t rawX); @@ -4716,6 +4746,14 @@ int32_t TouchInputMapperTest::toRawY(float displayY) { return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN); } +int32_t TouchInputMapperTest::toRotatedRawX(float displayX) { + return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_HEIGHT + RAW_X_MIN); +} + +int32_t TouchInputMapperTest::toRotatedRawY(float displayY) { + return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_WIDTH + RAW_Y_MIN); +} + float TouchInputMapperTest::toCookedX(float rawX, float rawY) { AFFINE_TRANSFORM.applyTo(rawX, rawY); return rawX; @@ -5370,11 +5408,12 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } -TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) { +TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_DoesNotRotateMotions) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareButtons(); prepareAxes(POSITION); - addConfigurationProperty("touch.orientationAware", "0"); + // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not + // need to be rotated. Touchscreens are orientation-aware by default. SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); NotifyMotionArgs args; @@ -5393,10 +5432,13 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotate ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); } -TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) { +TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_RotatesMotions) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareButtons(); prepareAxes(POSITION); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + addConfigurationProperty("touch.orientationAware", "0"); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); NotifyMotionArgs args; @@ -5418,7 +5460,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) // Rotation 90. clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_90); - processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50)); + processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); @@ -5446,7 +5488,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) // Rotation 270. clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_270); - processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN); + processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50)); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); @@ -5458,6 +5500,172 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); } +TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation0_RotatesMotions) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareButtons(); + prepareAxes(POSITION); + addConfigurationProperty("touch.orientationAware", "1"); + addConfigurationProperty("touch.orientation", "ORIENTATION_0"); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_0); + auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + NotifyMotionArgs args; + + // Orientation 0. + processDown(mapper, toRawX(50), toRawY(75)); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); +} + +TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation90_RotatesMotions) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareButtons(); + prepareAxes(POSITION); + addConfigurationProperty("touch.orientationAware", "1"); + addConfigurationProperty("touch.orientation", "ORIENTATION_90"); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_0); + auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + NotifyMotionArgs args; + + // Orientation 90. + processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50)); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); +} + +TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation180_RotatesMotions) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareButtons(); + prepareAxes(POSITION); + addConfigurationProperty("touch.orientationAware", "1"); + addConfigurationProperty("touch.orientation", "ORIENTATION_180"); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_0); + auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + NotifyMotionArgs args; + + // Orientation 180. + processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); +} + +TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation270_RotatesMotions) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareButtons(); + prepareAxes(POSITION); + addConfigurationProperty("touch.orientationAware", "1"); + addConfigurationProperty("touch.orientation", "ORIENTATION_270"); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_0); + auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + NotifyMotionArgs args; + + // Orientation 270. + processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); +} + +TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotionWithDisplay) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareButtons(); + prepareAxes(POSITION); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + addConfigurationProperty("touch.orientationAware", "0"); + addConfigurationProperty("touch.orientation", "ORIENTATION_90"); + auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + NotifyMotionArgs args; + + // Orientation 90, Rotation 0. + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_0); + processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50)); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); + + // Orientation 90, Rotation 90. + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_90); + processDown(mapper, toRotatedRawX(50), toRotatedRawY(75)); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); + + // Orientation 90, Rotation 180. + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_180); + processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); + + // Orientation 90, Rotation 270. + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_270); + processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN, + RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN); + processSync(mapper); + + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + processUp(mapper); + processSync(mapper); + EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); +} + TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); @@ -7808,7 +8016,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { ASSERT_EQ(std::vector<TouchVideoFrame>(), motionArgs.videoFrames); } -TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { +TEST_F(MultiTouchInputMapperTest, VideoFrames_AreNotRotated) { prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -7818,6 +8026,32 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { // Test all 4 orientations for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, + DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) { + SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation)); + clearViewports(); + prepareDisplay(orientation); + std::vector<TouchVideoFrame> frames{frame}; + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(frames, motionArgs.videoFrames); + } +} + +TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_AreRotated) { + prepareAxes(POSITION); + addConfigurationProperty("touch.deviceType", "touchScreen"); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + addConfigurationProperty("touch.orientationAware", "0"); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + // Unrotated video frame + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); + NotifyMotionArgs motionArgs; + + // Test all 4 orientations + for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) { SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation)); clearViewports(); @@ -7827,12 +8061,16 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - frames[0].rotate(orientation); + // We expect the raw coordinates of the MotionEvent to be rotated in the inverse direction + // compared to the display. This is so that when the window transform (which contains the + // display rotation) is applied later by InputDispatcher, the coordinates end up in the + // window's coordinate space. + frames[0].rotate(getInverseRotation(orientation)); ASSERT_EQ(frames, motionArgs.videoFrames); } } -TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { +TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreNotRotated) { prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -7849,8 +8087,36 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - std::for_each(frames.begin(), frames.end(), - [](TouchVideoFrame& frame) { frame.rotate(DISPLAY_ORIENTATION_90); }); + ASSERT_EQ(frames, motionArgs.videoFrames); +} + +TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_MultipleFramesAreRotated) { + prepareAxes(POSITION); + addConfigurationProperty("touch.deviceType", "touchScreen"); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + addConfigurationProperty("touch.orientationAware", "0"); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + // Unrotated video frames. There's no rule that they must all have the same dimensions, + // so mix these. + TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); + TouchVideoFrame frame2(3, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {1, 3}); + TouchVideoFrame frame3(2, 2, {10, 20, 10, 0}, {1, 4}); + std::vector<TouchVideoFrame> frames{frame1, frame2, frame3}; + NotifyMotionArgs motionArgs; + + prepareDisplay(DISPLAY_ORIENTATION_90); + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + std::for_each(frames.begin(), frames.end(), [](TouchVideoFrame& frame) { + // We expect the raw coordinates of the MotionEvent to be rotated in the inverse direction + // compared to the display. This is so that when the window transform (which contains the + // display rotation) is applied later by InputDispatcher, the coordinates end up in the + // window's coordinate space. + frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90)); + }); ASSERT_EQ(frames, motionArgs.videoFrames); } @@ -8404,18 +8670,25 @@ TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) { // Reset. mapper.reset(ARBITRARY_TIME); - // Let physical display be different to device, and make surface and physical could be 1:1. - halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_0); + // Let physical display be different to device, and make surface and physical could be 1:1 in + // all four orientations. + for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180, + DISPLAY_ORIENTATION_270}) { + halfDisplayToCenterHorizontal(orientation); - const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4); - const int32_t yExpected = y; - processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); + const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4); + const int32_t yExpected = y; + processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); + } } -TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) { +TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + addConfigurationProperty("touch.orientationAware", "0"); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees. @@ -8424,16 +8697,19 @@ TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) { const int32_t x = DISPLAY_WIDTH / 4; const int32_t y = DISPLAY_HEIGHT / 2; - // expect x/y = swap x/y then reverse y. - const int32_t xExpected = y; - const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1); + // expect x/y = swap x/y then reverse x. + constexpr int32_t xExpected = DISPLAY_HEIGHT - y; + constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4; processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); } -TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) { +TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + addConfigurationProperty("touch.orientationAware", "0"); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees. @@ -8442,16 +8718,19 @@ TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) { const int32_t x = DISPLAY_WIDTH / 4; const int32_t y = DISPLAY_HEIGHT / 2; - // expect x/y = swap x/y then reverse x. - constexpr int32_t xExpected = DISPLAY_HEIGHT - y; - constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4; + // expect x/y = swap x/y then reverse y. + const int32_t xExpected = y; + const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1); processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); } -TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner) { +TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + addConfigurationProperty("touch.orientationAware", "0"); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); const int32_t x = 0; @@ -8463,16 +8742,16 @@ TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner) { clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_90); - // expect x/y = swap x/y then reverse y. - const int32_t xExpected90 = y; - const int32_t yExpected90 = DISPLAY_WIDTH - 1; + // expect x/y = swap x/y then reverse x. + const int32_t xExpected90 = DISPLAY_HEIGHT - 1; + const int32_t yExpected90 = x; processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90); clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_270); - // expect x/y = swap x/y then reverse x. - const int32_t xExpected270 = DISPLAY_HEIGHT - 1; - const int32_t yExpected270 = x; + // expect x/y = swap x/y then reverse y. + const int32_t xExpected270 = y; + const int32_t yExpected270 = DISPLAY_WIDTH - 1; processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270); } diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp index df4db199f8..455a1e2133 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -42,4 +42,7 @@ cc_fuzz { srcs: [ "LatencyTrackerFuzzer.cpp", ], + fuzz_config: { + cc: ["android-framework-input@google.com"], + }, } diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index ec31afe367..e4efde2448 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -614,6 +614,8 @@ ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead Return<void> SensorDevice::onDynamicSensorsConnected( const hidl_vec<SensorInfo> &dynamicSensorsAdded) { + std::unique_lock<std::mutex> lock(mDynamicSensorsMutex); + // Allocate a sensor_t structure for each dynamic sensor added and insert // it into the dictionary of connected dynamic sensors keyed by handle. for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) { @@ -629,6 +631,8 @@ Return<void> SensorDevice::onDynamicSensorsConnected( std::make_pair(sensor->handle, sensor)); } + mDynamicSensorsCv.notify_all(); + return Return<void>(); } @@ -1174,8 +1178,20 @@ void SensorDevice::convertToSensorEvent( dst->dynamic_sensor_meta.connected = dyn.connected; dst->dynamic_sensor_meta.handle = dyn.sensorHandle; if (dyn.connected) { + std::unique_lock<std::mutex> lock(mDynamicSensorsMutex); + // Give MAX_DYN_SENSOR_WAIT_SEC for onDynamicSensorsConnected to be invoked since it + // can be received out of order from this event due to a bug in the HIDL spec that + // marks it as oneway. auto it = mConnectedDynamicSensors.find(dyn.sensorHandle); - CHECK(it != mConnectedDynamicSensors.end()); + if (it == mConnectedDynamicSensors.end()) { + mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT, + [&, dyn]{ + return mConnectedDynamicSensors.find(dyn.sensorHandle) + != mConnectedDynamicSensors.end(); + }); + it = mConnectedDynamicSensors.find(dyn.sensorHandle); + CHECK(it != mConnectedDynamicSensors.end()); + } dst->dynamic_sensor_meta.sensor = it->second; diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index 75da7bb0a1..bc8d20fbf6 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -139,6 +139,14 @@ private: Vector<sensor_t> mSensorList; std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors; + // A bug in the Sensors HIDL spec which marks onDynamicSensorsConnected as oneway causes dynamic + // meta events and onDynamicSensorsConnected to be received out of order. This mutex + CV are + // used to block meta event processing until onDynamicSensorsConnected is received to simplify + // HAL implementations. + std::mutex mDynamicSensorsMutex; + std::condition_variable mDynamicSensorsCv; + static constexpr std::chrono::seconds MAX_DYN_SENSOR_WAIT{5}; + static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz mutable Mutex mLock; // protect mActivationCount[].batchParams // fixed-size array after construction diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp index c285c00623..46f00e8329 100644 --- a/services/sensorservice/SensorInterface.cpp +++ b/services/sensorservice/SensorInterface.cpp @@ -92,31 +92,13 @@ ProximitySensor::ProximitySensor(const sensor_t& sensor, SensorService& service) } status_t ProximitySensor::activate(void* ident, bool enabled) { - bool lastState = mSensorDevice.isSensorActive(mSensor.getHandle()); - status_t status = HardwareSensor::activate(ident, enabled); if (status != NO_ERROR) { return status; } - - bool currentState = mSensorDevice.isSensorActive(mSensor.getHandle()); - if (currentState != lastState) { - mSensorService.onProximityActiveLocked(currentState); - } + mSensorService.checkAndReportProxStateChangeLocked(); return NO_ERROR; } -void ProximitySensor::willDisableAllSensors() { - if (mSensorDevice.isSensorActive(mSensor.getHandle())) { - mSensorService.onProximityActiveLocked(false); - } -} - -void ProximitySensor::didEnableAllSensors() { - if (mSensorDevice.isSensorActive(mSensor.getHandle())) { - mSensorService.onProximityActiveLocked(true); - } -} - // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h index 4e9f7bf9c8..57043592c5 100644 --- a/services/sensorservice/SensorInterface.h +++ b/services/sensorservice/SensorInterface.h @@ -44,9 +44,6 @@ public: virtual const Sensor& getSensor() const = 0; virtual bool isVirtual() const = 0; virtual void autoDisable(void* /*ident*/, int /*handle*/) = 0; - - virtual void willDisableAllSensors() = 0; - virtual void didEnableAllSensors() = 0; }; class BaseSensor : public SensorInterface { @@ -70,8 +67,6 @@ public: virtual const Sensor& getSensor() const override { return mSensor; } virtual void autoDisable(void* /*ident*/, int /*handle*/) override { } - virtual void willDisableAllSensors() override { } - virtual void didEnableAllSensors() override { } protected: SensorDevice& mSensorDevice; Sensor mSensor; @@ -115,8 +110,6 @@ public: status_t activate(void* ident, bool enabled) override; - void willDisableAllSensors() override; - void didEnableAllSensors() override; private: SensorService& mSensorService; }; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 726fe8ea84..bdbae7b1ec 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -103,7 +103,7 @@ static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSOR SensorService::SensorService() : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED), - mWakeLockAcquired(false), mProximityActiveCount(0) { + mWakeLockAcquired(false), mLastReportedProxIsActive(false) { mUidPolicy = new UidPolicy(this); mSensorPrivacyPolicy = new SensorPrivacyPolicy(this); } @@ -204,9 +204,11 @@ void SensorService::onFirstRef() { } if (useThisSensor) { if (list[i].type == SENSOR_TYPE_PROXIMITY) { - registerSensor(new ProximitySensor(list[i], *this)); + SensorInterface* s = new ProximitySensor(list[i], *this); + registerSensor(s); + mProxSensorHandles.push_back(s->getSensor().getHandle()); } else { - registerSensor( new HardwareSensor(list[i]) ); + registerSensor(new HardwareSensor(list[i])); } } } @@ -331,6 +333,7 @@ void SensorService::onUidStateChanged(uid_t uid, UidState state) { conn->onSensorAccessChanged(hasAccess); } } + checkAndReportProxStateChangeLocked(); } bool SensorService::hasSensorAccess(uid_t uid, const String16& opPackageName) { @@ -680,11 +683,8 @@ void SensorService::disableAllSensorsLocked(ConnectionSafeAutolock* connLock) { bool hasAccess = hasSensorAccessLocked(conn->getUid(), conn->getOpPackageName()); conn->onSensorAccessChanged(hasAccess); } - mSensors.forEachEntry([](const SensorServiceUtil::SensorList::Entry& e) { - e.si->willDisableAllSensors(); - return true; - }); dev.disableAllSensors(); + checkAndReportProxStateChangeLocked(); // Clear all pending flush connections for all active sensors. If one of the active // connections has called flush() and the underlying sensor has been disabled before a // flush complete event is returned, we need to remove the connection from this queue. @@ -709,14 +709,11 @@ void SensorService::enableAllSensorsLocked(ConnectionSafeAutolock* connLock) { } SensorDevice& dev(SensorDevice::getInstance()); dev.enableAllSensors(); - mSensors.forEachEntry([](const SensorServiceUtil::SensorList::Entry& e) { - e.si->didEnableAllSensors(); - return true; - }); for (const sp<SensorDirectConnection>& conn : connLock->getDirectConnections()) { bool hasAccess = hasSensorAccessLocked(conn->getUid(), conn->getOpPackageName()); conn->onSensorAccessChanged(hasAccess); } + checkAndReportProxStateChangeLocked(); } void SensorService::capRates(userid_t userId) { @@ -1538,10 +1535,7 @@ status_t SensorService::resetToNormalModeLocked() { if (err == NO_ERROR) { mCurrentOperatingMode = NORMAL; dev.enableAllSensors(); - mSensors.forEachEntry([](const SensorServiceUtil::SensorList::Entry& e) { - e.si->didEnableAllSensors(); - return true; - }); + checkAndReportProxStateChangeLocked(); } return err; } @@ -1606,28 +1600,26 @@ void SensorService::cleanupConnection(SensorDirectConnection* c) { mConnectionHolder.removeDirectConnection(c); } -void SensorService::onProximityActiveLocked(bool isActive) { - int prevCount = mProximityActiveCount; - bool activeStateChanged = false; - if (isActive) { - mProximityActiveCount++; - activeStateChanged = prevCount == 0; - } else { - mProximityActiveCount--; - if (mProximityActiveCount < 0) { - ALOGE("Proximity active count is negative (%d)!", mProximityActiveCount); +void SensorService::checkAndReportProxStateChangeLocked() { + if (mProxSensorHandles.empty()) return; + + SensorDevice& dev(SensorDevice::getInstance()); + bool isActive = false; + for (auto& sensor : mProxSensorHandles) { + if (dev.isSensorActive(sensor)) { + isActive = true; + break; } - activeStateChanged = prevCount > 0 && mProximityActiveCount <= 0; } - - if (activeStateChanged) { - notifyProximityStateLocked(mProximityActiveListeners); + if (isActive != mLastReportedProxIsActive) { + notifyProximityStateLocked(isActive, mProximityActiveListeners); + mLastReportedProxIsActive = isActive; } } void SensorService::notifyProximityStateLocked( + const bool isActive, const std::vector<sp<ProximityActiveListener>>& listeners) { - const bool isActive = mProximityActiveCount > 0; const uint64_t mySeq = ++curProxCallbackSeq; std::thread t([isActive, mySeq, listenersCopy = listeners]() { while (completedCallbackSeq.load() != mySeq - 1) @@ -1655,7 +1647,7 @@ status_t SensorService::addProximityActiveListener(const sp<ProximityActiveListe mProximityActiveListeners.push_back(callback); std::vector<sp<ProximityActiveListener>> listener(1, callback); - notifyProximityStateLocked(listener); + notifyProximityStateLocked(mLastReportedProxIsActive, listener); return OK; } diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index def661177c..b059e61130 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -103,8 +103,9 @@ public: void cleanupConnection(SensorDirectConnection* c); // Call with mLock held. - void onProximityActiveLocked(bool isActive); - void notifyProximityStateLocked(const std::vector<sp<ProximityActiveListener>>& listeners); + void checkAndReportProxStateChangeLocked(); + void notifyProximityStateLocked(const bool isActive, + const std::vector<sp<ProximityActiveListener>>& listeners); status_t enable(const sp<SensorEventConnection>& connection, int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags, @@ -496,8 +497,11 @@ private: // Checks if the mic sensor privacy is enabled for the uid bool isMicSensorPrivacyEnabledForUid(uid_t uid); - // Counts how many proximity sensors are currently active. - int mProximityActiveCount; + // Keeps track of the handles of all proximity sensors in the system. + std::vector<int32_t> mProxSensorHandles; + // The last proximity sensor active state reported to listeners. + bool mLastReportedProxIsActive; + // Listeners subscribed to receive updates on the proximity sensor active state. std::vector<sp<ProximityActiveListener>> mProximityActiveListeners; }; diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 40b1fd08ce..9e69283244 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -155,6 +155,7 @@ filegroup { "FrameTracer/FrameTracer.cpp", "FrameTracker.cpp", "HdrLayerInfoReporter.cpp", + "WindowInfosListenerInvoker.cpp", "Layer.cpp", "LayerProtoHelper.cpp", "LayerRejecter.cpp", @@ -209,7 +210,6 @@ cc_defaults { "libcutils", "libdisplayservicehidl", "libhidlbase", - "libinput", "liblayers_proto", "liblog", "libprocessgroup", diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index f2a69940af..636555e685 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -58,6 +58,8 @@ namespace android { +using gui::WindowInfo; + static constexpr float defaultMaxLuminance = 1000.0; BufferLayer::BufferLayer(const LayerCreationArgs& args) @@ -419,33 +421,35 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, mFrameTracker.setFrameReadyTime(desiredPresentTime); } - const Fps refreshRate = mFlinger->mRefreshRateConfigs->getCurrentRefreshRate().getFps(); - const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); - if (presentFence->isValid()) { - mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, - refreshRate, renderRate, - frameRateToSetFrameRateVotePayload( - mDrawingState.frameRate), - getGameMode()); - mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, - presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); - } else if (!display) { - // Do nothing. - } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); - displayId && mFlinger->getHwComposer().isConnected(*displayId)) { - // The HWC doesn't support present fences, so use the refresh - // timestamp instead. - const nsecs_t actualPresentTime = display->getRefreshTimestamp(); - mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, - refreshRate, renderRate, - frameRateToSetFrameRateVotePayload( - mDrawingState.frameRate), - getGameMode()); - mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, - actualPresentTime, + if (display) { + const Fps refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps(); + const std::optional<Fps> renderRate = + mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); + if (presentFence->isValid()) { + mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, + refreshRate, renderRate, + frameRateToSetFrameRateVotePayload( + mDrawingState.frameRate), + getGameMode()); + mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, + presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentTime(actualPresentTime); + mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); + } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); + displayId && mFlinger->getHwComposer().isConnected(*displayId)) { + // The HWC doesn't support present fences, so use the refresh + // timestamp instead. + const nsecs_t actualPresentTime = display->getRefreshTimestamp(); + mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, + refreshRate, renderRate, + frameRateToSetFrameRateVotePayload( + mDrawingState.frameRate), + getGameMode()); + mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), + mCurrentFrameNumber, actualPresentTime, + FrameTracer::FrameEvent::PRESENT_FENCE); + mFrameTracker.setActualPresentTime(actualPresentTime); + } } mFrameTracker.advanceFrame(); @@ -825,9 +829,9 @@ void BufferLayer::updateCloneBufferInfo() { wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives; wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; - InputWindowInfo tmpInputInfo = mDrawingState.inputInfo; + WindowInfo tmpInputInfo = mDrawingState.inputInfo; - mDrawingState = clonedFrom->mDrawingState; + cloneDrawingState(clonedFrom.get()); mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 6b6d43425d..99e470dfe6 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -515,13 +515,10 @@ void BufferQueueLayer::onFirstRef() { } status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) { - uint32_t const maxSurfaceDims = - std::min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims()); - // never allow a surface larger than what our underlying GL implementation // can handle. - if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) { - ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h)); + if (mFlinger->exceedsMaxRenderTargetSize(w, h)) { + ALOGE("dimensions too large %" PRIu32 " x %" PRIu32, w, h); return BAD_VALUE; } diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 8bc51dfa5d..df91904ac8 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -66,7 +66,7 @@ BufferStateLayer::~BufferStateLayer() { // one of the layers, in this case the original layer, needs to handle the deletion. The // original layer and the clone should be removed at the same time so there shouldn't be any // issue with the clone layer trying to use the texture. - if (mBufferInfo.mBuffer != nullptr && !isClone()) { + if (mBufferInfo.mBuffer != nullptr) { callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, mBufferInfo.mFence, mTransformHint, @@ -158,7 +158,8 @@ void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { // transaction doesn't need a previous release fence. sp<CallbackHandle> ch; for (auto& handle : mDrawingState.callbackHandles) { - if (handle->releasePreviousBuffer) { + if (handle->releasePreviousBuffer && + mDrawingState.releaseBufferEndpoint == handle->listener) { ch = handle; break; } @@ -199,14 +200,9 @@ void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); } - // If there are multiple transactions in this frame, set the previous id on the earliest - // transacton. We don't need to pass in the released buffer id to multiple transactions. - // The buffer id does not have to correspond to any particular transaction as long as the - // listening end point is the same but the client expects the first transaction callback that - // replaces the presented buffer to contain the release fence. This follows the same logic. - // see BufferStateLayer::onLayerDisplayed. for (auto& handle : mDrawingState.callbackHandles) { - if (handle->releasePreviousBuffer) { + if (handle->releasePreviousBuffer && + mDrawingState.releaseBufferEndpoint == handle->listener) { handle->previousReleaseCallbackId = mPreviousReleaseCallbackId; break; } @@ -420,7 +416,8 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex nsecs_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber, std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info, - const sp<ITransactionCompletedListener>& releaseBufferListener) { + const sp<ITransactionCompletedListener>& releaseBufferListener, + const sp<IBinder>& releaseBufferEndpoint) { ATRACE_CALL(); if (mDrawingState.buffer) { @@ -485,6 +482,7 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex mDrawingState.width = mDrawingState.buffer->getBuffer()->getWidth(); mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight(); + mDrawingState.releaseBufferEndpoint = releaseBufferEndpoint; return true; } diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index cab48994b3..0a0527c1a9 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -59,7 +59,8 @@ public: const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber, std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info, - const sp<ITransactionCompletedListener>& transactionListener) override; + const sp<ITransactionCompletedListener>& transactionListener, + const sp<IBinder>& releaseBufferEndpoint) override; bool setAcquireFence(const sp<Fence>& fence) override; bool setDataspace(ui::Dataspace dataspace) override; bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 1416b1e3c0..0ef0b995c3 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -166,6 +166,9 @@ public: // Enables (or disables) layer caching on this output virtual void setLayerCachingEnabled(bool) = 0; + // Enables (or disables) layer caching texture pool on this output + virtual void setLayerCachingTexturePoolEnabled(bool) = 0; + // Sets the projection state to use virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, const Rect& orientedDisplaySpaceRect) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index f832084f39..ddcc907a91 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -41,6 +41,7 @@ public: std::optional<DisplayId> getDisplayId() const override; void setCompositionEnabled(bool) override; void setLayerCachingEnabled(bool) override; + void setLayerCachingTexturePoolEnabled(bool) override; void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, const Rect& orientedDisplaySpaceRect) override; void setDisplaySize(const ui::Size&) override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index 7534548d4a..a040fa93de 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -38,35 +38,56 @@ class Predictor; class Flattener { public: - struct CachedSetRenderSchedulingTunables { - // This default assumes that rendering a cached set takes about 3ms. That time is then cut - // in half - the next frame using the cached set would have the same workload, meaning that - // composition cost is the same. This is best illustrated with the following example: - // - // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If - // renderCachedSets costs 3ms, then two consecutive frames have timings: - // - // First frame: Start at 0ms, end at 6.8ms. - // renderCachedSets: Start at 6.8ms, end at 9.8ms. - // Second frame: Start at 9.8ms, end at 16.6ms. - // - // Now the second frame won't render a cached set afterwards, but the first frame didn't - // really steal time from the second frame. - static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration = 1500us; - - static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240; - - // Duration allocated for rendering a cached set. If we don't have enough time for rendering - // a cached set, then rendering is deferred to another frame. - const std::chrono::nanoseconds cachedSetRenderDuration; - // Maximum of times that we defer rendering a cached set. If we defer rendering a cached set - // too many times, then render it anyways so that future frames would benefit from the - // flattened cached set. - const size_t maxDeferRenderAttempts; + // Collection of tunables which are backed by sysprops + struct Tunables { + // Tunables that are specific to scheduling when a cached set should be rendered + struct RenderScheduling { + // This default assumes that rendering a cached set takes about 3ms. That time is then + // cut in half - the next frame using the cached set would have the same workload, + // meaning that composition cost is the same. This is best illustrated with the + // following example: + // + // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If + // renderCachedSets costs 3ms, then two consecutive frames have timings: + // + // First frame: Start at 0ms, end at 6.8ms. + // renderCachedSets: Start at 6.8ms, end at 9.8ms. + // Second frame: Start at 9.8ms, end at 16.6ms. + // + // Now the second frame won't render a cached set afterwards, but the first frame didn't + // really steal time from the second frame. + static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration = + 1500us; + + static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240; + + // Duration allocated for rendering a cached set. If we don't have enough time for + // rendering a cached set, then rendering is deferred to another frame. + const std::chrono::nanoseconds cachedSetRenderDuration; + // Maximum of times that we defer rendering a cached set. If we defer rendering a cached + // set too many times, then render it anyways so that future frames would benefit from + // the flattened cached set. + const size_t maxDeferRenderAttempts; + }; + + static const constexpr std::chrono::milliseconds kDefaultActiveLayerTimeout = 150ms; + + static const constexpr bool kDefaultEnableHolePunch = true; + + // Threshold for determing whether a layer is active. A layer whose properties, including + // the buffer, have not changed in at least this time is considered inactive and is + // therefore a candidate for flattening. + const std::chrono::milliseconds mActiveLayerTimeout; + + // Toggles for scheduling when it's safe to render a cached set. + // See: RenderScheduling + const std::optional<RenderScheduling> mRenderScheduling; + + // True if the hole punching feature should be enabled. + const bool mEnableHolePunch; }; - Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false, - std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables = - std::nullopt); + + Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables); void setDisplaySize(ui::Size size) { mDisplaySize = size; @@ -80,6 +101,8 @@ public: void renderCachedSets(const OutputCompositionState& outputState, std::optional<std::chrono::steady_clock::time_point> renderDeadline); + void setTexturePoolEnabled(bool enabled) { mTexturePool.setEnabled(enabled); } + void dump(std::string& result) const; void dumpLayers(std::string& result) const; @@ -175,8 +198,7 @@ private: void buildCachedSets(std::chrono::steady_clock::time_point now); renderengine::RenderEngine& mRenderEngine; - const bool mEnableHolePunch; - const std::optional<CachedSetRenderSchedulingTunables> mCachedSetRenderSchedulingTunables; + const Tunables mTunables; TexturePool mTexturePool; @@ -200,9 +222,6 @@ private: size_t mCachedSetCreationCount = 0; size_t mCachedSetCreationCost = 0; std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges; - std::chrono::nanoseconds mActiveLayerTimeout = kActiveLayerTimeout; - - static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms); }; } // namespace compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index bce438fa13..523752719d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -21,7 +21,7 @@ #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> -#include <input/Flags.h> +#include <ftl/Flags.h> #include <string> diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h index be34153d31..b7ebca60fd 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h @@ -39,6 +39,9 @@ namespace compositionengine::impl::planner { // heuristically determining the composition strategy of the current layer stack, // and flattens inactive layers into an override buffer so it can be used // as a more efficient representation of parts of the layer stack. +// Implicitly, layer caching must also be enabled for the Planner to have any effect +// E.g., setprop debug.sf.enable_layer_caching 1, or +// adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>] class Planner { public: Planner(renderengine::RenderEngine& renderengine); @@ -64,6 +67,8 @@ public: void renderCachedSets(const OutputCompositionState& outputState, std::optional<std::chrono::steady_clock::time_point> renderDeadline); + void setTexturePoolEnabled(bool enabled) { mFlattener.setTexturePoolEnabled(enabled); } + void dump(const Vector<String16>& args, std::string&); private: diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h index fb53ee04cd..d607c75325 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h @@ -63,7 +63,8 @@ public: sp<Fence> mFence; }; - TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine) {} + TexturePool(renderengine::RenderEngine& renderEngine) + : mRenderEngine(renderEngine), mEnabled(false) {} virtual ~TexturePool() = default; @@ -78,6 +79,12 @@ public: // to the pool. std::shared_ptr<AutoTexture> borrowTexture(); + // Enables or disables the pool. When the pool is disabled, no buffers will + // be held by the pool. This is useful when the active display changes. + void setEnabled(bool enable); + + void dump(std::string& out) const; + protected: // Proteted visibility so that they can be used for testing const static constexpr size_t kMinPoolSize = 3; @@ -95,8 +102,10 @@ private: // Returns a previously borrowed texture to the pool. void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture, const sp<Fence>& fence); + void allocatePool(); renderengine::RenderEngine& mRenderEngine; ui::Size mSize; + bool mEnabled; }; } // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 8e777e3c9f..8fdf3ae887 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -37,6 +37,7 @@ public: MOCK_METHOD1(setCompositionEnabled, void(bool)); MOCK_METHOD1(setLayerCachingEnabled, void(bool)); + MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool)); MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&)); MOCK_METHOD1(setDisplaySize, void(const ui::Size&)); MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool)); diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index cafcb40e80..95ae5e514e 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -146,6 +146,12 @@ void Output::setLayerCachingEnabled(bool enabled) { } } +void Output::setLayerCachingTexturePoolEnabled(bool enabled) { + if (mPlanner) { + mPlanner->setTexturePoolEnabled(enabled); + } +} + void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, const Rect& orientedDisplaySpaceRect) { auto& outputState = editState(); @@ -783,6 +789,9 @@ compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition if (compState->sidebandStream != nullptr) { return nullptr; } + if (compState->isOpaque) { + continue; + } if (compState->backgroundBlurRadius > 0 || compState->blurRegions.size() > 0) { layerRequestingBgComposition = layer; } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index f033279caa..ad5e93168d 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -60,19 +60,8 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, } // namespace -Flattener::Flattener( - renderengine::RenderEngine& renderEngine, bool enableHolePunch, - std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables) - : mRenderEngine(renderEngine), - mEnableHolePunch(enableHolePunch), - mCachedSetRenderSchedulingTunables(cachedSetRenderSchedulingTunables), - mTexturePool(mRenderEngine) { - const int timeoutInMs = - base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0); - if (timeoutInMs != 0) { - mActiveLayerTimeout = std::chrono::milliseconds(timeoutInMs); - } -} +Flattener::Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables) + : mRenderEngine(renderEngine), mTunables(tunables), mTexturePool(mRenderEngine) {} NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash hash, time_point now) { @@ -128,14 +117,14 @@ void Flattener::renderCachedSets( // If we have a render deadline, and the flattener is configured to skip rendering if we don't // have enough time, then we skip rendering the cached set if we think that we'll steal too much // time from the next frame. - if (renderDeadline && mCachedSetRenderSchedulingTunables) { + if (renderDeadline && mTunables.mRenderScheduling) { if (const auto estimatedRenderFinish = - now + mCachedSetRenderSchedulingTunables->cachedSetRenderDuration; + now + mTunables.mRenderScheduling->cachedSetRenderDuration; estimatedRenderFinish > *renderDeadline) { mNewCachedSet->incrementSkipCount(); if (mNewCachedSet->getSkipCount() <= - mCachedSetRenderSchedulingTunables->maxDeferRenderAttempts) { + mTunables.mRenderScheduling->maxDeferRenderAttempts) { ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us", std::chrono::duration_cast<std::chrono::microseconds>( estimatedRenderFinish - *renderDeadline) @@ -205,6 +194,9 @@ void Flattener::dump(std::string& result) const { durationString(lastUpdate).c_str()); dumpLayers(result); + + base::StringAppendF(&result, "\n"); + mTexturePool.dump(result); } size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const { @@ -417,8 +409,10 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { bool runHasFirstLayer = false; for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) { - const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout; + const bool layerIsInactive = + now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout; const bool layerHasBlur = currentSet->hasBlurBehind(); + if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) && !currentSet->hasUnsupportedDataspace()) { if (isPartOfRun) { @@ -519,7 +513,7 @@ void Flattener::buildCachedSets(time_point now) { mNewCachedSet->addBackgroundBlurLayer(*bestRun->getBlurringLayer()); } - if (mEnableHolePunch && bestRun->getHolePunchCandidate() && + if (mTunables.mEnableHolePunch && bestRun->getHolePunchCandidate() && bestRun->getHolePunchCandidate()->requiresHolePunch()) { // Add the pip layer to mNewCachedSet, but in a special way - it should // replace the buffer with a clear round rect. diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index f077470c80..f5b1cee469 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -32,36 +32,46 @@ namespace android::compositionengine::impl::planner { namespace { -std::optional<Flattener::CachedSetRenderSchedulingTunables> buildFlattenerTuneables() { +std::optional<Flattener::Tunables::RenderScheduling> buildRenderSchedulingTunables() { if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) { return std::nullopt; } - auto renderDuration = std::chrono::nanoseconds( + const auto renderDuration = std::chrono::nanoseconds( base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"), - Flattener::CachedSetRenderSchedulingTunables:: + Flattener::Tunables::RenderScheduling:: kDefaultCachedSetRenderDuration.count())); - auto maxDeferRenderAttempts = base::GetUintProperty< + const auto maxDeferRenderAttempts = base::GetUintProperty< size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"), - Flattener::CachedSetRenderSchedulingTunables::kDefaultMaxDeferRenderAttempts); + Flattener::Tunables::RenderScheduling::kDefaultMaxDeferRenderAttempts); - return std::make_optional<Flattener::CachedSetRenderSchedulingTunables>( - Flattener::CachedSetRenderSchedulingTunables{ + return std::make_optional<Flattener::Tunables::RenderScheduling>( + Flattener::Tunables::RenderScheduling{ .cachedSetRenderDuration = renderDuration, .maxDeferRenderAttempts = maxDeferRenderAttempts, }); } +Flattener::Tunables buildFlattenerTuneables() { + const auto activeLayerTimeout = std::chrono::milliseconds( + base::GetIntProperty<int32_t>(std::string( + "debug.sf.layer_caching_active_layer_timeout_ms"), + Flattener::Tunables::kDefaultActiveLayerTimeout.count())); + const auto enableHolePunch = + base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), + Flattener::Tunables::kDefaultEnableHolePunch); + return Flattener::Tunables{ + .mActiveLayerTimeout = activeLayerTimeout, + .mRenderScheduling = buildRenderSchedulingTunables(), + .mEnableHolePunch = enableHolePunch, + }; +} + } // namespace Planner::Planner(renderengine::RenderEngine& renderEngine) - // Implicitly, layer caching must also be enabled for the hole punch or - // predictor to have any effect. - // E.g., setprop debug.sf.enable_layer_caching 1, or - // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>] : mFlattener(renderEngine, - base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true), buildFlattenerTuneables()) { mPredictorEnabled = base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false); diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp index e3772a22d2..497c433c76 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp @@ -24,14 +24,22 @@ namespace android::compositionengine::impl::planner { +void TexturePool::allocatePool() { + mPool.clear(); + if (mEnabled && mSize.isValid()) { + mPool.resize(kMinPoolSize); + std::generate_n(mPool.begin(), kMinPoolSize, [&]() { + return Entry{genTexture(), nullptr}; + }); + } +} + void TexturePool::setDisplaySize(ui::Size size) { if (mSize == size) { return; } mSize = size; - mPool.clear(); - mPool.resize(kMinPoolSize); - std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; }); + allocatePool(); } std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() { @@ -46,7 +54,12 @@ std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() { void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture, const sp<Fence>& fence) { - // Drop the texture on the floor if the pool is no longer tracking textures of the same size. + // Drop the texture on the floor if the pool is not enabled + if (!mEnabled) { + return; + } + + // Or the texture on the floor if the pool is no longer tracking textures of the same size. if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() || static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) { ALOGV("Deallocating texture from Planner's pool - display size changed (previous: (%dx%d), " @@ -81,4 +94,15 @@ std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() { renderengine::ExternalTexture::Usage::WRITEABLE); } +void TexturePool::setEnabled(bool enabled) { + mEnabled = enabled; + allocatePool(); +} + +void TexturePool::dump(std::string& out) const { + base::StringAppendF(&out, + "TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n", + mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height); +} + } // namespace android::compositionengine::impl::planner
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index a195e5808a..ada5adfa12 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -47,9 +47,7 @@ public: MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t()); MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t()); - MOCK_METHOD4(allocateVirtualDisplay, - bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*, - std::optional<PhysicalDisplayId>)); + MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*)); MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId)); MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId)); MOCK_METHOD5(getDeviceCompositionChanges, diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index ee73cfc0c1..09f5a5e51d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -3606,6 +3606,7 @@ struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur : public OutputComposeSurfacesTest_SetsExpensiveRendering { OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() { mLayer.layerFEState.backgroundBlurRadius = 10; + mLayer.layerFEState.isOpaque = false; mOutput.editState().isEnabled = true; EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); @@ -4225,6 +4226,37 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace)); } +TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) { + InjectedLayer layer1; + InjectedLayer layer2; + + uint32_t z = 0; + // Layer requesting blur, or below, should request client composition, unless opaque. + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, + writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, + writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + + layer2.layerFEState.backgroundBlurRadius = 10; + layer2.layerFEState.isOpaque = true; + + injectOutputLayer(layer1); + injectOutputLayer(layer2); + + mOutput->editState().isEnabled = true; + + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateCompositionState(args); + mOutput->planComposition(); + mOutput->writeCompositionState(args); +} + TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) { InjectedLayer layer1; InjectedLayer layer2; @@ -4246,6 +4278,7 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); layer2.layerFEState.backgroundBlurRadius = 10; + layer2.layerFEState.isOpaque = false; injectOutputLayer(layer1); injectOutputLayer(layer2); @@ -4283,6 +4316,7 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { BlurRegion region; layer2.layerFEState.blurRegions.push_back(region); + layer2.layerFEState.isOpaque = false; injectOutputLayer(layer1); injectOutputLayer(layer2); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index f5cfd2f115..a28fb2c652 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -47,23 +47,24 @@ namespace { class TestableFlattener : public Flattener { public: - TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch, - std::optional<Flattener::CachedSetRenderSchedulingTunables> - cachedSetRenderSchedulingTunables = std::nullopt) - : Flattener(renderEngine, enableHolePunch, cachedSetRenderSchedulingTunables) {} + TestableFlattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables) + : Flattener(renderEngine, tunables) {} const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; } }; class FlattenerTest : public testing::Test { public: - FlattenerTest() : FlattenerTest(std::nullopt) {} + FlattenerTest() + : FlattenerTest(Flattener::Tunables{ + .mActiveLayerTimeout = 100ms, + .mRenderScheduling = std::nullopt, + .mEnableHolePunch = true, + }) {} void SetUp() override; protected: - FlattenerTest(std::optional<Flattener::CachedSetRenderSchedulingTunables> - cachedSetRenderSchedulingTunables) - : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, true, - cachedSetRenderSchedulingTunables)) {} + FlattenerTest(const Flattener::Tunables& tunables) + : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, tunables)) {} void initializeOverrideBuffer(const std::vector<const LayerState*>& layers); void initializeFlattener(const std::vector<const LayerState*>& layers); void expectAllLayersFlattened(const std::vector<const LayerState*>& layers); @@ -899,11 +900,13 @@ class FlattenerRenderSchedulingTest : public FlattenerTest { public: FlattenerRenderSchedulingTest() : FlattenerTest( - Flattener::CachedSetRenderSchedulingTunables{.cachedSetRenderDuration = + Flattener::Tunables{.mActiveLayerTimeout = 100ms, + .mRenderScheduling = Flattener::Tunables:: + RenderScheduling{.cachedSetRenderDuration = kCachedSetRenderDuration, .maxDeferRenderAttempts = - kMaxDeferRenderAttempts}) { - } + kMaxDeferRenderAttempts}, + .mEnableHolePunch = true}) {} }; TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToMaxAttempts) { diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp index b802e51234..6fc90fe5e5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp @@ -42,6 +42,7 @@ struct TexturePoolTest : public testing::Test { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + mTexturePool.setEnabled(true); mTexturePool.setDisplaySize(kDisplaySize); } @@ -130,5 +131,44 @@ TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { static_cast<int32_t>(texture->get()->getBuffer()->getHeight())); } +TEST_F(TexturePoolTest, freesBuffersWhenDisabled) { + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); + + std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; + for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { + textures.emplace_back(mTexturePool.borrowTexture()); + } + + EXPECT_EQ(mTexturePool.getPoolSize(), 1u); + mTexturePool.setEnabled(false); + EXPECT_EQ(mTexturePool.getPoolSize(), 0u); + + textures.clear(); + EXPECT_EQ(mTexturePool.getPoolSize(), 0u); +} + +TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) { + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); + mTexturePool.setEnabled(false); + EXPECT_EQ(mTexturePool.getPoolSize(), 0u); + + std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; + for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { + textures.emplace_back(mTexturePool.borrowTexture()); + } + + EXPECT_EQ(mTexturePool.getPoolSize(), 0u); + textures.clear(); + EXPECT_EQ(mTexturePool.getPoolSize(), 0u); +} + +TEST_F(TexturePoolTest, reallocatesWhenReEnabled) { + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); + mTexturePool.setEnabled(false); + EXPECT_EQ(mTexturePool.getPoolSize(), 0u); + mTexturePool.setEnabled(true); + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); +} + } // namespace } // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index ca4b6abc03..4445eea604 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -22,6 +22,8 @@ #undef LOG_TAG #define LOG_TAG "DisplayDevice" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + #include <android-base/stringprintf.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> @@ -40,6 +42,7 @@ #include "DisplayDevice.h" #include "Layer.h" +#include "RefreshRateOverlay.h" #include "SurfaceFlinger.h" namespace android { @@ -65,9 +68,12 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mSequenceId(args.sequenceId), mConnectionType(args.connectionType), mCompositionDisplay{args.compositionDisplay}, + mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())), + mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())), mPhysicalOrientation(args.physicalOrientation), mSupportedModes(std::move(args.supportedModes)), - mIsPrimary(args.isPrimary) { + mIsPrimary(args.isPrimary), + mRefreshRateConfigs(std::move(args.refreshRateConfigs)) { mCompositionDisplay->editState().isSecure = args.isSecure; mCompositionDisplay->createRenderSurface( compositionengine::RenderSurfaceCreationArgsBuilder() @@ -154,20 +160,29 @@ bool DisplayDevice::isPoweredOn() const { void DisplayDevice::setActiveMode(DisplayModeId id) { const auto mode = getMode(id); LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported."); + ATRACE_INT(mActiveModeFPSTrace.c_str(), mode->getFps().getIntValue()); mActiveMode = mode; + if (mRefreshRateConfigs) { + mRefreshRateConfigs->setCurrentModeId(mActiveMode->getId()); + } + if (mRefreshRateOverlay) { + mRefreshRateOverlay->changeRefreshRate(mActiveMode->getFps()); + } } -status_t DisplayDevice::initiateModeChange(DisplayModeId modeId, +status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info, const hal::VsyncPeriodChangeConstraints& constraints, - hal::VsyncPeriodChangeTimeline* outTimeline) const { - const auto mode = getMode(modeId); - if (!mode) { + hal::VsyncPeriodChangeTimeline* outTimeline) { + if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) { ALOGE("Trying to initiate a mode change to invalid mode %s on display %s", - std::to_string(modeId.value()).c_str(), to_string(getId()).c_str()); + info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null", + to_string(getId()).c_str()); return BAD_VALUE; } - return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), mode->getHwcId(), constraints, - outTimeline); + mUpcomingActiveMode = info; + ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue()); + return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(), + constraints, outTimeline); } const DisplayModePtr& DisplayDevice::getActiveMode() const { @@ -217,12 +232,23 @@ ui::Dataspace DisplayDevice::getCompositionDataSpace() const { } void DisplayDevice::setLayerStack(ui::LayerStack stack) { - mCompositionDisplay->setLayerStackFilter(stack, isPrimary()); + mCompositionDisplay->setLayerStackFilter(stack, isInternal()); + if (mRefreshRateOverlay) { + mRefreshRateOverlay->setLayerStack(stack); + } +} + +void DisplayDevice::setFlags(uint32_t flags) { + mFlags = flags; } void DisplayDevice::setDisplaySize(int width, int height) { LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays."); - mCompositionDisplay->setDisplaySize(ui::Size(width, height)); + const auto size = ui::Size(width, height); + mCompositionDisplay->setDisplaySize(size); + if (mRefreshRateOverlay) { + mRefreshRateOverlay->setViewport(size); + } } void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect, @@ -262,7 +288,7 @@ ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() { std::string DisplayDevice::getDebugName() const { const char* type = "virtual"; if (mConnectionType) { - type = *mConnectionType == ui::DisplayConnectionType::Internal ? "internal" : "external"; + type = isInternal() ? "internal" : "external"; } return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type, @@ -292,6 +318,10 @@ void DisplayDevice::dump(std::string& result) const { } result.append("\n"); getCompositionDisplay()->dump(result); + + if (mRefreshRateConfigs) { + mRefreshRateConfigs->dump(result); + } } bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const { @@ -378,6 +408,80 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { capabilities.getDesiredMinLuminance()); } +void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) { + if (!enable) { + mRefreshRateOverlay.reset(); + return; + } + + const auto [lowFps, highFps] = mRefreshRateConfigs->getSupportedRefreshRateRange(); + mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*mFlinger, lowFps.getIntValue(), + highFps.getIntValue(), showSpinnner); + mRefreshRateOverlay->setLayerStack(getLayerStack()); + mRefreshRateOverlay->setViewport(getSize()); + mRefreshRateOverlay->changeRefreshRate(getActiveMode()->getFps()); +} + +bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId, + bool timerExpired) { + if (mRefreshRateConfigs && mRefreshRateOverlay) { + const auto newRefreshRate = + mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired); + if (newRefreshRate) { + mRefreshRateOverlay->changeRefreshRate(*newRefreshRate); + return true; + } + } + + return false; +} + +void DisplayDevice::onInvalidate() { + if (mRefreshRateOverlay) { + mRefreshRateOverlay->onInvalidate(); + } +} + +bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) { + ATRACE_CALL(); + + LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided"); + LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch"); + + ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str()); + + std::scoped_lock lock(mActiveModeLock); + if (mDesiredActiveModeChanged) { + // If a mode change is pending, just cache the latest request in mDesiredActiveMode + const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event; + mDesiredActiveMode = info; + mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig; + return false; + } + + // Check if we are already at the desired mode + if (getActiveMode()->getId() == info.mode->getId()) { + return false; + } + + // Initiate a mode change. + mDesiredActiveModeChanged = true; + mDesiredActiveMode = info; + return true; +} + +std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const { + std::scoped_lock lock(mActiveModeLock); + if (mDesiredActiveModeChanged) return mDesiredActiveMode; + return std::nullopt; +} + +void DisplayDevice::clearDesiredActiveModeState() { + std::scoped_lock lock(mActiveModeLock); + mDesiredActiveMode.event = Scheduler::ModeEvent::None; + mDesiredActiveModeChanged = false; +} + std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 7e4d92308c..4d435c7e47 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -39,17 +39,24 @@ #include <utils/RefBase.h> #include <utils/Timers.h> +#include "MainThreadGuard.h" + #include "DisplayHardware/DisplayIdentification.h" #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/Hal.h" #include "DisplayHardware/PowerAdvisor.h" +#include "Scheduler/RefreshRateConfigs.h" + +#include "TracedOrdinal.h" + namespace android { class Fence; class HWComposer; class IGraphicBufferProducer; class Layer; +class RefreshRateOverlay; class SurfaceFlinger; struct CompositionInfo; @@ -64,6 +71,7 @@ class DisplayDevice : public RefBase { public: constexpr static float sDefaultMinLumiance = 0.0; constexpr static float sDefaultMaxLumiance = 500.0; + enum { eReceivesInput = 0x01 }; explicit DisplayDevice(DisplayDeviceCreationArgs& args); @@ -78,6 +86,9 @@ public: bool isVirtual() const { return !mConnectionType; } bool isPrimary() const { return mIsPrimary; } + bool isInternal() const { + return !isVirtual() && mConnectionType == ui::DisplayConnectionType::Internal; + } // isSecure indicates whether this display can be trusted to display // secure surfaces. @@ -90,6 +101,7 @@ public: void setLayerStack(ui::LayerStack); void setDisplaySize(int width, int height); void setProjection(ui::Rotation orientation, Rect viewport, Rect frame); + void setFlags(uint32_t flags); ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; } ui::Rotation getOrientation() const { return mOrientation; } @@ -102,6 +114,7 @@ public: const Rect& getOrientedDisplaySpaceRect() const; bool needsFiltering() const; ui::LayerStack getLayerStack() const; + bool receivesInput() const { return mFlags & eReceivesInput; } DisplayId getId() const; @@ -172,10 +185,28 @@ public: * Display mode management. */ const DisplayModePtr& getActiveMode() const; - void setActiveMode(DisplayModeId); - status_t initiateModeChange(DisplayModeId modeId, + + struct ActiveModeInfo { + DisplayModePtr mode; + scheduler::RefreshRateConfigEvent event = scheduler::RefreshRateConfigEvent::None; + + bool operator!=(const ActiveModeInfo& other) const { + return mode != other.mode || event != other.event; + } + }; + + bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock); + std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock); + void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock); + ActiveModeInfo getUpcomingActiveMode() const REQUIRES(SF_MAIN_THREAD) { + return mUpcomingActiveMode; + } + + void setActiveMode(DisplayModeId) REQUIRES(SF_MAIN_THREAD); + status_t initiateModeChange(const ActiveModeInfo&, const hal::VsyncPeriodChangeConstraints& constraints, - hal::VsyncPeriodChangeTimeline* outTimeline) const; + hal::VsyncPeriodChangeTimeline* outTimeline) + REQUIRES(SF_MAIN_THREAD); // Return the immutable list of supported display modes. The HWC may report different modes // after a hotplug reconnect event, in which case the DisplayDevice object will be recreated. @@ -187,6 +218,22 @@ public: // set-top boxes after a hotplug reconnect. DisplayModePtr getMode(DisplayModeId) const; + // Returns the refresh rate configs for this display. + scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; } + + // Returns a shared pointer to the refresh rate configs for this display. + // Clients can store this refresh rate configs and use it even if the DisplayDevice + // is destroyed. + std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const { + return mRefreshRateConfigs; + } + + // Enables an overlay to be displayed with the current refresh rate + void enableRefreshRateOverlay(bool enable, bool showSpinner); + bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; } + bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired); + void onInvalidate(); + void onVsync(nsecs_t timestamp); nsecs_t getVsyncPeriodFromHWC() const; nsecs_t getRefreshTimestamp() const; @@ -211,6 +258,8 @@ private: const std::shared_ptr<compositionengine::Display> mCompositionDisplay; std::string mDisplayName; + std::string mActiveModeFPSTrace; + std::string mActiveModeFPSHwcTrace; const ui::Rotation mPhysicalOrientation; ui::Rotation mOrientation = ui::ROTATION_0; @@ -227,9 +276,20 @@ private: // TODO(b/74619554): Remove special cases for primary display. const bool mIsPrimary; + uint32_t mFlags = 0; + std::optional<DeviceProductInfo> mDeviceProductInfo; std::vector<ui::Hdr> mOverrideHdrTypes; + + std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay; + + mutable std::mutex mActiveModeLock; + ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock); + TracedOrdinal<bool> mDesiredActiveModeChanged + GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false}; + ActiveModeInfo mUpcomingActiveMode GUARDED_BY(SF_MAIN_THREAD); }; struct DisplayDeviceState { @@ -252,6 +312,7 @@ struct DisplayDeviceState { std::optional<Physical> physical; sp<IGraphicBufferProducer> surface; ui::LayerStack layerStack = ui::NO_LAYER_STACK; + uint32_t flags = 0; Rect layerStackSpaceRect; Rect orientedDisplaySpaceRect; ui::Rotation orientation = ui::ROTATION_0; @@ -274,6 +335,7 @@ struct DisplayDeviceCreationArgs { HWComposer& hwComposer; const wp<IBinder> displayToken; const std::shared_ptr<compositionengine::Display> compositionDisplay; + std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs; int32_t sequenceId{0}; std::optional<ui::DisplayConnectionType> connectionType; @@ -289,6 +351,7 @@ struct DisplayDeviceCreationArgs { hardware::graphics::composer::hal::PowerMode::ON}; bool isPrimary{false}; DisplayModes supportedModes; + DisplayModeId activeModeId; }; // Predicates for display lookup. diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index caf0294a56..09734c275f 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -212,7 +212,7 @@ uint32_t Composer::getMaxVirtualDisplayCount() } Error Composer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, - std::optional<Display>, Display* outDisplay) { + Display* outDisplay) { const uint32_t bufferSlotCount = 1; Error error = kDefaultError; if (mClient_2_2) { diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index b525e63c66..fe114b9512 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -96,7 +96,7 @@ public: virtual uint32_t getMaxVirtualDisplayCount() = 0; virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*, - std::optional<Display> mirror, Display* outDisplay) = 0; + Display* outDisplay) = 0; virtual Error destroyVirtualDisplay(Display display) = 0; virtual Error acceptDisplayChanges(Display display) = 0; @@ -342,7 +342,7 @@ public: uint32_t getMaxVirtualDisplayCount() override; Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, - std::optional<Display> mirror, Display* outDisplay) override; + Display* outDisplay) override; Error destroyVirtualDisplay(Display display) override; Error acceptDisplayChanges(Display display) override; diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h index 85cc993c67..5de622b318 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayMode.h +++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h @@ -22,6 +22,7 @@ #include <android-base/stringprintf.h> #include <android/configuration.h> +#include <ui/DisplayId.h> #include <ui/DisplayMode.h> #include <ui/Size.h> #include <utils/Timers.h> @@ -54,6 +55,11 @@ public: return *this; } + Builder& setPhysicalDisplayId(PhysicalDisplayId id) { + mDisplayMode->mPhysicalDisplayId = id; + return *this; + } + Builder& setWidth(int32_t width) { mDisplayMode->mWidth = width; return *this; @@ -112,6 +118,7 @@ public: DisplayModeId getId() const { return mId; } hal::HWConfigId getHwcId() const { return mHwcId; } + PhysicalDisplayId getPhysicalDisplayId() const { return mPhysicalDisplayId; } int32_t getWidth() const { return mWidth; } int32_t getHeight() const { return mHeight; } @@ -136,6 +143,7 @@ private: hal::HWConfigId mHwcId; DisplayModeId mId; + PhysicalDisplayId mPhysicalDisplayId; int32_t mWidth = -1; int32_t mHeight = -1; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 7e45dabdea..a790b4c11e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -245,8 +245,7 @@ size_t HWComposer::getMaxVirtualDisplayDimension() const { } bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size resolution, - ui::PixelFormat* format, - std::optional<PhysicalDisplayId> mirror) { + ui::PixelFormat* format) { if (!resolution.isValid()) { ALOGE("%s: Invalid resolution %dx%d", __func__, resolution.width, resolution.height); return false; @@ -262,14 +261,9 @@ bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size return false; } - std::optional<hal::HWDisplayId> hwcMirrorId; - if (mirror) { - hwcMirrorId = fromPhysicalDisplayId(*mirror); - } - hal::HWDisplayId hwcDisplayId; const auto error = static_cast<hal::Error>( - mComposer->createVirtualDisplay(width, height, format, hwcMirrorId, &hwcDisplayId)); + mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId)); RETURN_IF_HWC_ERROR_FOR("createVirtualDisplay", error, displayId, false); auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index b1849e8c11..49f96d9614 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -113,11 +113,7 @@ public: // Attempts to allocate a virtual display on the HWC. The maximum number of virtual displays // supported by the HWC can be queried in advance, but allocation may fail for other reasons. - // For virtualized compositors, the PhysicalDisplayId is a hint that this virtual display is - // a mirror of a physical display, and that the screen should be captured by the host rather - // than guest compositor. - virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*, - std::optional<PhysicalDisplayId> mirror) = 0; + virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0; virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0; @@ -265,8 +261,7 @@ public: size_t getMaxVirtualDisplayCount() const override; size_t getMaxVirtualDisplayDimension() const override; - bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*, - std::optional<PhysicalDisplayId>) override; + bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override; // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated. void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override; diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h index bb2888e0fb..02d065881b 100644 --- a/services/surfaceflinger/DisplayHardware/Hal.h +++ b/services/surfaceflinger/DisplayHardware/Hal.h @@ -166,4 +166,15 @@ inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) } } +inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) { + switch (vsync) { + case hardware::graphics::composer::hal::Vsync::ENABLE: + return "Enable"; + case hardware::graphics::composer::hal::Vsync::DISABLE: + return "Disable"; + default: + return "Unknown"; + } +} + } // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index d8cead5e50..da7ff71510 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -71,7 +71,6 @@ #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" #include "TunnelModeEnabledReporter.h" -#include "input/InputWindow.h" #define DEBUG_RESIZE 0 @@ -83,6 +82,7 @@ constexpr int kDumpTableRowLength = 159; using base::StringAppendF; using namespace android::flag_operators; using PresentState = frametimeline::SurfaceFrame::PresentState; +using gui::WindowInfo; std::atomic<int32_t> Layer::sSequence{1}; @@ -90,8 +90,8 @@ Layer::Layer(const LayerCreationArgs& args) : mFlinger(args.flinger), mName(args.name), mClientRef(args.client), - mWindowType(static_cast<InputWindowInfo::Type>( - args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) { + mWindowType( + static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) { uint32_t layerFlags = 0; if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque; @@ -135,6 +135,7 @@ Layer::Layer(const LayerCreationArgs& args) mDrawingState.postTime = -1; mDrawingState.destinationFrame.makeInvalid(); mDrawingState.isTrustedOverlay = false; + mDrawingState.dropInputMode = gui::DropInputMode::NONE; if (args.flags & ISurfaceComposerClient::eNoColorFill) { // Set an invalid color so there is no color fill. @@ -421,8 +422,6 @@ void Layer::prepareBasicGeometryCompositionState() { compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); compositionState->alpha = alpha; - compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; - compositionState->blurRegions = drawingState.blurRegions; compositionState->stretchEffect = getStretchEffect(); } @@ -498,6 +497,9 @@ void Layer::preparePerFrameCompositionState() { compositionState->stretchEffect.hasEffect()) { compositionState->forceClientComposition = true; } + // If there are no visible region changes, we still need to update blur parameters. + compositionState->blurRegions = drawingState.blurRegions; + compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; } void Layer::prepareCursorCompositionState() { @@ -938,8 +940,11 @@ bool Layer::setCornerRadius(float cornerRadius) { bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) { if (mDrawingState.backgroundBlurRadius == backgroundBlurRadius) return false; - - mDrawingState.sequence++; + // If we start or stop drawing blur then the layer's visibility state may change so increment + // the magic sequence number. + if (mDrawingState.backgroundBlurRadius == 0 || backgroundBlurRadius == 0) { + mDrawingState.sequence++; + } mDrawingState.backgroundBlurRadius = backgroundBlurRadius; mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -972,6 +977,11 @@ bool Layer::setTransparentRegionHint(const Region& transparent) { } bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) { + // If we start or stop drawing blur then the layer's visibility state may change so increment + // the magic sequence number. + if (mDrawingState.blurRegions.size() == 0 || blurRegions.size() == 0) { + mDrawingState.sequence++; + } mDrawingState.blurRegions = blurRegions; mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -1916,27 +1926,49 @@ const std::vector<BlurRegion> Layer::getBlurRegions() const { } Layer::RoundedCornerState Layer::getRoundedCornerState() const { - const auto& p = mDrawingParent.promote(); - if (p != nullptr) { - RoundedCornerState parentState = p->getRoundedCornerState(); - if (parentState.radius > 0) { + // Get parent settings + RoundedCornerState parentSettings; + const auto& parent = mDrawingParent.promote(); + if (parent != nullptr) { + parentSettings = parent->getRoundedCornerState(); + if (parentSettings.radius > 0) { ui::Transform t = getActiveTransform(getDrawingState()); t = t.inverse(); - parentState.cropRect = t.transform(parentState.cropRect); + parentSettings.cropRect = t.transform(parentSettings.cropRect); // The rounded corners shader only accepts 1 corner radius for performance reasons, // but a transform matrix can define horizontal and vertical scales. // Let's take the average between both of them and pass into the shader, practically we // never do this type of transformation on windows anyway. auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]); auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]); - parentState.radius *= (scaleX + scaleY) / 2.0f; - return parentState; + parentSettings.radius *= (scaleX + scaleY) / 2.0f; } } + + // Get layer settings + Rect layerCropRect = getCroppedBufferSize(getDrawingState()); const float radius = getDrawingState().cornerRadius; - return radius > 0 && getCroppedBufferSize(getDrawingState()).isValid() - ? RoundedCornerState(getCroppedBufferSize(getDrawingState()).toFloatRect(), radius) - : RoundedCornerState(); + RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius); + const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid(); + + if (layerSettingsValid && parentSettings.radius > 0) { + // If the parent and the layer have rounded corner settings, use the parent settings if the + // parent crop is entirely inside the layer crop. + // This has limitations and cause rendering artifacts. See b/200300845 for correct fix. + if (parentSettings.cropRect.left > layerCropRect.left && + parentSettings.cropRect.top > layerCropRect.top && + parentSettings.cropRect.right < layerCropRect.right && + parentSettings.cropRect.bottom < layerCropRect.bottom) { + return parentSettings; + } else { + return layerSettings; + } + } else if (layerSettingsValid) { + return layerSettings; + } else if (parentSettings.radius > 0) { + return parentSettings; + } + return {}; } void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster, @@ -1982,7 +2014,7 @@ void Layer::commitChildList() { } -void Layer::setInputInfo(const InputWindowInfo& info) { +void Layer::setInputInfo(const WindowInfo& info) { mDrawingState.inputInfo = info; mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote()); mDrawingState.modified = true; @@ -2021,8 +2053,8 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, if (buffer != nullptr) { LayerProtoHelper::writeToProto(buffer, [&]() { return layerInfo->mutable_active_buffer(); }); - LayerProtoHelper::writeToProto(ui::Transform(getBufferTransform()), - layerInfo->mutable_buffer_transform()); + LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()), + layerInfo->mutable_buffer_transform()); } layerInfo->set_invalidate(contentDirty); layerInfo->set_is_protected(isProtected()); @@ -2032,10 +2064,11 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, layerInfo->set_curr_frame(mCurrentFrameNumber); layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); + layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius); layerInfo->set_corner_radius(getRoundedCornerState().radius); layerInfo->set_background_blur_radius(getBackgroundBlurRadius()); layerInfo->set_is_trusted_overlay(isTrustedOverlay()); - LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform()); + LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform()); LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), [&]() { return layerInfo->mutable_position(); }); LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); @@ -2110,8 +2143,8 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet [&]() { return layerInfo->mutable_requested_color(); }); layerInfo->set_flags(state.flags); - LayerProtoHelper::writeToProto(requestedTransform, - layerInfo->mutable_requested_transform()); + LayerProtoHelper::writeToProtoDeprecated(requestedTransform, + layerInfo->mutable_requested_transform()); auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); if (parent != nullptr) { @@ -2133,7 +2166,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet } if (traceFlags & SurfaceTracing::TRACE_INPUT) { - InputWindowInfo info; + WindowInfo info; if (useDrawing) { info = fillInputInfo({nullptr}); } else { @@ -2164,7 +2197,7 @@ Rect Layer::getInputBounds() const { return getCroppedBufferSize(getDrawingState()); } -void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) { +void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotatedDisplay) { // Transform layer size to screen space and inset it by surface insets. // If this is a portal window, set the touchableRegion to the layerBounds. Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE @@ -2181,16 +2214,18 @@ void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhy info.frameRight = 0; info.frameBottom = 0; info.transform.reset(); + info.touchableRegion = Region(); + info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE; return; } ui::Transform layerToDisplay = getInputTransform(); - // Transform that takes window coordinates to unrotated display coordinates - ui::Transform t = toPhysicalDisplay * layerToDisplay; + // Transform that takes window coordinates to non-rotated display coordinates + ui::Transform t = toNonRotatedDisplay * layerToDisplay; int32_t xSurfaceInset = info.surfaceInset; int32_t ySurfaceInset = info.surfaceInset; - // Bring screenBounds into unrotated space - Rect screenBounds = toPhysicalDisplay.transform(Rect{mScreenBounds}); + // Bring screenBounds into non-rotated space + Rect screenBounds = toNonRotatedDisplay.transform(Rect{mScreenBounds}); const float xScale = t.getScaleX(); const float yScale = t.getScaleY(); @@ -2259,7 +2294,7 @@ void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhy info.touchableRegion = inputTransform.transform(info.touchableRegion); } -void Layer::fillTouchOcclusionMode(InputWindowInfo& info) { +void Layer::fillTouchOcclusionMode(WindowInfo& info) { sp<Layer> p = this; while (p != nullptr && !p->hasInputInfo()) { p = p->mDrawingParent.promote(); @@ -2269,32 +2304,115 @@ void Layer::fillTouchOcclusionMode(InputWindowInfo& info) { } } -InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { +gui::DropInputMode Layer::getDropInputMode() const { + gui::DropInputMode mode = mDrawingState.dropInputMode; + if (mode == gui::DropInputMode::ALL) { + return mode; + } + sp<Layer> parent = mDrawingParent.promote(); + if (parent) { + gui::DropInputMode parentMode = parent->getDropInputMode(); + if (parentMode != gui::DropInputMode::NONE) { + return parentMode; + } + } + return mode; +} + +void Layer::handleDropInputMode(gui::WindowInfo& info) const { + if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) { + return; + } + + // Check if we need to drop input unconditionally + gui::DropInputMode dropInputMode = getDropInputMode(); + if (dropInputMode == gui::DropInputMode::ALL) { + info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + ALOGV("Dropping input for %s as requested by policy.", getDebugName()); + return; + } + + // Check if we need to check if the window is obscured by parent + if (dropInputMode != gui::DropInputMode::OBSCURED) { + return; + } + + // Check if the parent has set an alpha on the layer + sp<Layer> parent = mDrawingParent.promote(); + if (parent && parent->getAlpha() != 1.0_hf) { + info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(), + static_cast<float>(getAlpha())); + } + + // Check if the parent has cropped the buffer + Rect bufferSize = getCroppedBufferSize(getDrawingState()); + if (!bufferSize.isValid()) { + info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED; + return; + } + + // Screenbounds are the layer bounds cropped by parents, transformed to screenspace. + // To check if the layer has been cropped, we take the buffer bounds, apply the local + // layer crop and apply the same set of transforms to move to screenspace. If the bounds + // match then the layer has not been cropped by its parents. + Rect bufferInScreenSpace(getTransform().transform(bufferSize)); + bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds}; + + if (croppedByParent) { + info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent", + getDebugName()); + } else { + // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop + // input if the window is obscured. This check should be done in surfaceflinger but the + // logic currently resides in inputflinger. So pass the if_obscured check to input to only + // drop input events if the window is obscured. + info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED; + } +} + +WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { if (!hasInputInfo()) { mDrawingState.inputInfo.name = getName(); mDrawingState.inputInfo.ownerUid = mOwnerUid; mDrawingState.inputInfo.ownerPid = mOwnerPid; - mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL; - mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL; + mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL; + mDrawingState.inputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL; mDrawingState.inputInfo.displayId = getLayerStack(); } - InputWindowInfo info = mDrawingState.inputInfo; + WindowInfo info = mDrawingState.inputInfo; info.id = sequence; + info.displayId = getLayerStack(); - if (info.displayId == ADISPLAY_ID_NONE) { - info.displayId = getLayerStack(); - } - - // Transform that goes from "logical(rotated)" display to physical/unrotated display. - // This is for when inputflinger operates in physical display-space. - ui::Transform toPhysicalDisplay; + // Transform that goes from "logical(rotated)" display to the non-rotated display. + ui::Transform toNonRotatedDisplay; if (display) { - toPhysicalDisplay = display->getTransform(); - info.displayWidth = display->getWidth(); - info.displayHeight = display->getHeight(); + // The physical orientation is set when the orientation of the display panel is different + // than the default orientation of the device. We do not need to expose the physical + // orientation of the panel outside of SurfaceFlinger. + const ui::Rotation inversePhysicalOrientation = + ui::ROTATION_0 - display->getPhysicalOrientation(); + auto width = display->getWidth(); + auto height = display->getHeight(); + if (inversePhysicalOrientation == ui::ROTATION_90 || + inversePhysicalOrientation == ui::ROTATION_270) { + std::swap(width, height); + } + const auto rotationFlags = ui::Transform::toRotationFlags(inversePhysicalOrientation); + const ui::Transform undoPhysicalOrientation(rotationFlags, width, height); + toNonRotatedDisplay = undoPhysicalOrientation * display->getTransform(); + + // Send the inverse of the display orientation so that input can transform points back to + // the rotated display space. + const ui::Rotation inverseOrientation = ui::ROTATION_0 - display->getOrientation(); + info.displayOrientation = ui::Transform::toRotationFlags(inverseOrientation); + + info.displayWidth = width; + info.displayHeight = height; } - fillInputFrameInfo(info, toPhysicalDisplay); + fillInputFrameInfo(info, toNonRotatedDisplay); // For compatibility reasons we let layers which can receive input // receive input before they have actually submitted a buffer. Because @@ -2307,18 +2425,19 @@ InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { info.visible = hasInputInfo() ? canReceiveInput() : isVisible(); info.alpha = getAlpha(); fillTouchOcclusionMode(info); + handleDropInputMode(info); auto cropLayer = mDrawingState.touchableRegionCrop.promote(); if (info.replaceTouchableRegionWithCrop) { if (cropLayer == nullptr) { - info.touchableRegion = Region(toPhysicalDisplay.transform(Rect{mScreenBounds})); + info.touchableRegion = Region(toNonRotatedDisplay.transform(Rect{mScreenBounds})); } else { info.touchableRegion = - Region(toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds})); + Region(toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds})); } } else if (cropLayer != nullptr) { info.touchableRegion = info.touchableRegion.intersect( - toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds})); + toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds})); } // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state @@ -2331,7 +2450,7 @@ InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { if (isClone()) { sp<Layer> clonedRoot = getClonedRoot(); if (clonedRoot != nullptr) { - Rect rect = toPhysicalDisplay.transform(Rect{clonedRoot->mScreenBounds}); + Rect rect = toNonRotatedDisplay.transform(Rect{clonedRoot->mScreenBounds}); info.touchableRegion = info.touchableRegion.intersect(rect); } } @@ -2369,8 +2488,7 @@ Region Layer::getVisibleRegion(const DisplayDevice* display) const { } void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) { - // copy drawing state from cloned layer - mDrawingState = clonedFrom->mDrawingState; + cloneDrawingState(clonedFrom.get()); mClonedFrom = clonedFrom; } @@ -2405,7 +2523,7 @@ void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayer // since we may be able to pull out other children that are still alive. if (isClonedFromAlive()) { sp<Layer> clonedFrom = getClonedFrom(); - mDrawingState = clonedFrom->mDrawingState; + cloneDrawingState(clonedFrom.get()); clonedLayersMap.emplace(clonedFrom, this); } @@ -2458,7 +2576,7 @@ void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLa } // Cloned layers shouldn't handle watch outside since their z order is not determined by // WM or the client. - mDrawingState.inputInfo.flags &= ~InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH; + mDrawingState.inputInfo.flags &= ~WindowInfo::Flag::WATCH_OUTSIDE_TOUCH; } void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { @@ -2560,6 +2678,21 @@ wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) { return handle->owner; } +bool Layer::setDropInputMode(gui::DropInputMode mode) { + if (mDrawingState.dropInputMode == mode) { + return false; + } + mDrawingState.dropInputMode = mode; + return true; +} + +void Layer::cloneDrawingState(const Layer* from) { + mDrawingState = from->mDrawingState; + // Skip callback info since they are not applicable for cloned layers. + mDrawingState.releaseBufferListener = nullptr; + mDrawingState.callbackHandles = {}; +} + // --------------------------------------------------------------------------- std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 8905548b64..c1af4685f0 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -17,11 +17,12 @@ #pragma once +#include <android/gui/DropInputMode.h> #include <compositionengine/LayerFE.h> #include <gui/BufferQueue.h> #include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> -#include <input/InputWindow.h> +#include <gui/WindowInfo.h> #include <layerproto/LayerProtoHeader.h> #include <math/vec4.h> #include <renderengine/Mesh.h> @@ -186,7 +187,7 @@ public: float cornerRadius; int backgroundBlurRadius; - InputWindowInfo inputInfo; + gui::WindowInfo inputInfo; wp<Layer> touchableRegionCrop; // dataspace is only used by BufferStateLayer and EffectLayer @@ -280,6 +281,10 @@ public: Rect bufferCrop; Rect destinationFrame; + + sp<IBinder> releaseBufferEndpoint; + + gui::DropInputMode dropInputMode; }; /* @@ -422,7 +427,8 @@ public: const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/, - const sp<ITransactionCompletedListener>& /* releaseBufferListener */) { + const sp<ITransactionCompletedListener>& /* releaseBufferListener */, + const sp<IBinder>& /* releaseBufferEndpoint */) { return false; }; virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; }; @@ -444,6 +450,8 @@ public: virtual bool setFrameRateSelectionPriority(int32_t priority); virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint); virtual void setAutoRefresh(bool /* autoRefresh */) {} + bool setDropInputMode(gui::DropInputMode); + // If the variable is not set on the layer, it traverses up the tree to inherit the frame // rate priority from its parent. virtual int32_t getFrameRateSelectionPriority() const; @@ -603,10 +611,8 @@ public: virtual bool getTransformToDisplayInverse() const { return false; } // Returns how rounded corners should be drawn for this layer. - // This will traverse the hierarchy until it reaches its root, finding topmost rounded - // corner definition and converting it into current layer's coordinates. - // As of now, only 1 corner radius per display list is supported. Subsequent ones will be - // ignored. + // A layer can override its parent's rounded corner settings if the parent's rounded + // corner crop does not intersect with its own rounded corner crop. virtual RoundedCornerState getRoundedCornerState() const; bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; } @@ -695,7 +701,7 @@ public: void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet, uint32_t traceFlags = SurfaceTracing::TRACE_ALL); - InputWindowInfo::Type getWindowType() const { return mWindowType; } + gui::WindowInfo::Type getWindowType() const { return mWindowType; } bool getPrimaryDisplayOnly() const; @@ -846,9 +852,9 @@ public: sp<IBinder> getHandle(); const std::string& getName() const { return mName; } bool getPremultipledAlpha() const; - void setInputInfo(const InputWindowInfo& info); + void setInputInfo(const gui::WindowInfo& info); - InputWindowInfo fillInputInfo(const sp<DisplayDevice>& display); + gui::WindowInfo fillInputInfo(const sp<DisplayDevice>& display); /** * Returns whether this layer has an explicitly set input-info. */ @@ -923,6 +929,7 @@ protected: bool isClone() { return mClonedFrom != nullptr; } bool isClonedFromAlive() { return getClonedFrom() != nullptr; } + void cloneDrawingState(const Layer* from); void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); void updateClonedChildren(const sp<Layer>& mirrorRoot, std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); @@ -1013,7 +1020,7 @@ protected: wp<Layer> mDrawingParent; // Window types from WindowManager.LayoutParams - const InputWindowInfo::Type mWindowType; + const gui::WindowInfo::Type mWindowType; // The owner of the layer. If created from a non system process, it will be the calling uid. // If created from a system process, the value can be passed in. @@ -1056,6 +1063,8 @@ private: bool setFrameRateForLayerTree(FrameRate); void setZOrderRelativeOf(const wp<Layer>& relativeOf); bool isTrustedOverlay() const; + gui::DropInputMode getDropInputMode() const; + void handleDropInputMode(gui::WindowInfo& info) const; // Find the root of the cloned hierarchy, this means the first non cloned parent. // This will return null if first non cloned parent is not found. @@ -1067,10 +1076,10 @@ private: // Fills in the touch occlusion mode of the first parent (including this layer) that // hasInputInfo() or no-op if no such parent is found. - void fillTouchOcclusionMode(InputWindowInfo& info); + void fillTouchOcclusionMode(gui::WindowInfo& info); - // Fills in the frame and transform info for the InputWindowInfo - void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay); + // Fills in the frame and transform info for the gui::WindowInfo + void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toNonRotatedDisplay); // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index b1db6d34a3..1062126b58 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -22,6 +22,9 @@ #include "LayerProtoHelper.h" namespace android { + +using gui::WindowInfo; + namespace surfaceflinger { void LayerProtoHelper::writePositionToProto(const float x, const float y, @@ -95,8 +98,8 @@ void LayerProtoHelper::writeToProto(const half4 color, std::function<ColorProto* } } -void LayerProtoHelper::writeToProto(const ui::Transform& transform, - TransformProto* transformProto) { +void LayerProtoHelper::writeToProtoDeprecated(const ui::Transform& transform, + TransformProto* transformProto) { const uint32_t type = transform.getType() | (transform.getOrientation() << 8); transformProto->set_type(type); @@ -111,6 +114,22 @@ void LayerProtoHelper::writeToProto(const ui::Transform& transform, } } +void LayerProtoHelper::writeTransformToProto(const ui::Transform& transform, + TransformProto* transformProto) { + const uint32_t type = transform.getType() | (transform.getOrientation() << 8); + transformProto->set_type(type); + + // Rotations that are 90/180/270 have their own type so the transform matrix can be + // reconstructed later. All other rotation have the type UNKNOWN so we need to save the + // transform values in that case. + if (type & (ui::Transform::SCALE | ui::Transform::UNKNOWN)) { + transformProto->set_dsdx(transform.dsdx()); + transformProto->set_dtdx(transform.dtdx()); + transformProto->set_dtdy(transform.dtdy()); + transformProto->set_dsdy(transform.dsdy()); + } +} + void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer, std::function<ActiveBufferProto*()> getActiveBufferProto) { if (buffer->getWidth() != 0 || buffer->getHeight() != 0 || buffer->getStride() != 0 || @@ -125,7 +144,7 @@ void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer, } void LayerProtoHelper::writeToProto( - const InputWindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds, + const WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds, std::function<InputWindowInfoProto*()> getInputWindowInfoProto) { if (inputInfo.token == nullptr) { return; @@ -133,7 +152,7 @@ void LayerProtoHelper::writeToProto( InputWindowInfoProto* proto = getInputWindowInfoProto(); proto->set_layout_params_flags(inputInfo.flags.get()); - using U = std::underlying_type_t<InputWindowInfo::Type>; + using U = std::underlying_type_t<WindowInfo::Type>; // TODO(b/129481165): This static assert can be safely removed once conversion warnings // are re-enabled. static_assert(std::is_same_v<U, int32_t>); @@ -151,7 +170,7 @@ void LayerProtoHelper::writeToProto( proto->set_has_wallpaper(inputInfo.hasWallpaper); proto->set_global_scale_factor(inputInfo.globalScaleFactor); - LayerProtoHelper::writeToProto(inputInfo.transform, proto->mutable_transform()); + LayerProtoHelper::writeToProtoDeprecated(inputInfo.transform, proto->mutable_transform()); proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop); auto cropLayer = touchableRegionBounds.promote(); if (cropLayer != nullptr) { diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 502238d389..36e06471ee 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -17,7 +17,7 @@ #include <layerproto/LayerProtoHeader.h> #include <Layer.h> -#include <input/InputWindow.h> +#include <gui/WindowInfo.h> #include <math/vec4.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> @@ -37,10 +37,15 @@ public: std::function<FloatRectProto*()> getFloatRectProto); static void writeToProto(const Region& region, std::function<RegionProto*()> getRegionProto); static void writeToProto(const half4 color, std::function<ColorProto*()> getColorProto); - static void writeToProto(const ui::Transform& transform, TransformProto* transformProto); + // This writeToProto for transform is incorrect, but due to backwards compatibility, we can't + // update Layers to use it. Use writeTransformToProto for any new transform proto data. + static void writeToProtoDeprecated(const ui::Transform& transform, + TransformProto* transformProto); + static void writeTransformToProto(const ui::Transform& transform, + TransformProto* transformProto); static void writeToProto(const sp<GraphicBuffer>& buffer, std::function<ActiveBufferProto*()> getActiveBufferProto); - static void writeToProto(const InputWindowInfo& inputInfo, + static void writeToProto(const gui::WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds, std::function<InputWindowInfoProto*()> getInputWindowInfoProto); static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto); diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp index e84508febc..11fe6d0755 100644 --- a/services/surfaceflinger/LayerRenderArea.cpp +++ b/services/surfaceflinger/LayerRenderArea.cpp @@ -94,8 +94,22 @@ void LayerRenderArea::render(std::function<void()> drawLayers) { // no need to check rotation because there is none mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight(); + // If layer is offscreen, update mirroring info if it exists + if (mLayer->isRemovedFromCurrentState()) { + mLayer->traverse(LayerVector::StateSet::Drawing, + [&](Layer* layer) { layer->updateMirrorInfo(); }); + mLayer->traverse(LayerVector::StateSet::Drawing, + [&](Layer* layer) { layer->updateCloneBufferInfo(); }); + } + if (!mChildrenOnly) { mTransform = mLayer->getTransform().inverse(); + // If the layer is offscreen, compute bounds since we don't compute bounds for offscreen + // layers in a regular cycles. + if (mLayer->isRemovedFromCurrentState()) { + FloatRect maxBounds = mFlinger.getMaxDisplayBounds(); + mLayer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */); + } drawLayers(); } else { uint32_t w = static_cast<uint32_t>(getWidth()); diff --git a/services/surfaceflinger/MainThreadGuard.h b/services/surfaceflinger/MainThreadGuard.h new file mode 100644 index 0000000000..c1aa118492 --- /dev/null +++ b/services/surfaceflinger/MainThreadGuard.h @@ -0,0 +1,35 @@ +/* + * 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 <utils/Mutex.h> + +namespace android { +namespace { + +// Helps to ensure that some functions runs on SF's main thread by using the +// clang thread safety annotations. +class CAPABILITY("mutex") MainThreadGuard { +} SF_MAIN_THREAD; + +struct SCOPED_CAPABILITY MainThreadScopedGuard { +public: + explicit MainThreadScopedGuard(MainThreadGuard& mutex) ACQUIRE(mutex) {} + ~MainThreadScopedGuard() RELEASE() {} +}; +} // namespace +} // namespace android diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 43a6e55a09..2ece51c3dc 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -3,4 +3,5 @@ alecmouri@google.com chaviw@google.com lpy@google.com racarr@google.com -vishnun@google.com +scroggo@google.com +vishnun@google.com
\ No newline at end of file diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 27a1c280fb..bc32a1d66c 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -175,10 +175,14 @@ std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumbe return buffers; } -RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner) - : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) { +RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, uint32_t lowFps, uint32_t highFps, + bool showSpinner) + : mFlinger(flinger), + mClient(new Client(&mFlinger)), + mShowSpinner(showSpinner), + mLowFps(lowFps), + mHighFps(highFps) { createLayer(); - reset(); } bool RefreshRateOverlay::createLayer() { @@ -194,7 +198,6 @@ bool RefreshRateOverlay::createLayer() { return false; } - Mutex::Autolock _l(mFlinger.mStateLock); mLayer = mClient->getLayerUser(mIBinder); mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote)); mLayer->setIsAtRoot(true); @@ -241,8 +244,11 @@ RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { } void RefreshRateOverlay::setViewport(ui::Size viewport) { - Rect frame((3 * viewport.width) >> 4, viewport.height >> 5); - frame.offsetBy(viewport.width >> 5, viewport.height >> 4); + constexpr int32_t kMaxWidth = 1000; + const auto width = std::min(kMaxWidth, std::min(viewport.width, viewport.height)); + const auto height = 2 * width; + Rect frame((3 * width) >> 4, height >> 5); + frame.offsetBy(width >> 5, height >> 4); layer_state_t::matrix22_t matrix; matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth()); @@ -254,13 +260,18 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } +void RefreshRateOverlay::setLayerStack(uint32_t stack) { + mLayer->setLayerStack(stack); + mFlinger.mTransactionFlags.fetch_or(eTransactionMask); +} + void RefreshRateOverlay::changeRefreshRate(const Fps& fps) { mCurrentFps = fps.getIntValue(); auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame]; mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), std::nullopt /* dequeueTime */, FrameTimelineInfo{}, - nullptr /* releaseBufferListener */); + nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } @@ -274,18 +285,11 @@ void RefreshRateOverlay::onInvalidate() { mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), std::nullopt /* dequeueTime */, FrameTimelineInfo{}, - nullptr /* releaseBufferListener */); + nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } -void RefreshRateOverlay::reset() { - mBufferCache.clear(); - const auto range = mFlinger.mRefreshRateConfigs->getSupportedRefreshRateRange(); - mLowFps = range.min.getIntValue(); - mHighFps = range.max.getIntValue(); -} - } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index aa8329c46a..f9baa898dc 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -37,12 +37,12 @@ class SurfaceFlinger; class RefreshRateOverlay { public: - RefreshRateOverlay(SurfaceFlinger&, bool showSpinner); + RefreshRateOverlay(SurfaceFlinger&, uint32_t lowFps, uint32_t highFps, bool showSpinner); + void setLayerStack(uint32_t stack); void setViewport(ui::Size); void changeRefreshRate(const Fps&); void onInvalidate(); - void reset(); private: class SevenSegmentDrawer { @@ -91,8 +91,8 @@ private: const bool mShowSpinner; // Interpolate the colors between these values. - uint32_t mLowFps; - uint32_t mHighFps; + const uint32_t mLowFps; + const uint32_t mHighFps; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 2321e2d082..2bdcaf6ad0 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -118,12 +118,12 @@ DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t times return event; } -DisplayEventReceiver::Event makeModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId, - nsecs_t vsyncPeriod) { +DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) { DisplayEventReceiver::Event event; - event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, displayId, systemTime()}; - event.modeChange.modeId = modeId.value(); - event.modeChange.vsyncPeriod = vsyncPeriod; + event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(), + systemTime()}; + event.modeChange.modeId = mode->getId().value(); + event.modeChange.vsyncPeriod = mode->getVsyncPeriod(); return event; } @@ -375,11 +375,10 @@ void EventThread::onHotplugReceived(PhysicalDisplayId displayId, bool connected) mCondition.notify_all(); } -void EventThread::onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId, - nsecs_t vsyncPeriod) { +void EventThread::onModeChanged(DisplayModePtr mode) { std::lock_guard<std::mutex> lock(mMutex); - mPendingEvents.push_back(makeModeChanged(displayId, modeId, vsyncPeriod)); + mPendingEvents.push_back(makeModeChanged(mode)); mCondition.notify_all(); } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 1e6793f77c..9265a25b86 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -124,8 +124,7 @@ public: virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0; // called when SF changes the active mode and apps needs to be notified about the change - virtual void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId, - nsecs_t vsyncPeriod) = 0; + virtual void onModeChanged(DisplayModePtr) = 0; // called when SF updates the Frame Rate Override list virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId, @@ -174,8 +173,7 @@ public: void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override; - void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId, - nsecs_t vsyncPeriod) override; + void onModeChanged(DisplayModePtr) override; void onFrameRateOverridesChanged(PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) override; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 0563795c79..84e3548b6e 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -75,10 +75,9 @@ void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) { } } // namespace -LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs) +LayerHistory::LayerHistory() : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) { LayerInfo::setTraceEnabled(mTraceEnabled); - LayerInfo::setRefreshRateConfigs(refreshRateConfigs); } LayerHistory::~LayerHistory() = default; @@ -138,7 +137,8 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, } } -LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { +LayerHistory::Summary LayerHistory::summarize(const RefreshRateConfigs& refreshRateConfigs, + nsecs_t now) { LayerHistory::Summary summary; std::lock_guard lock(mLock); @@ -151,7 +151,7 @@ LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority, layerFocused ? "" : "not"); - const auto vote = info->getRefreshRateVote(now); + const auto vote = info->getRefreshRateVote(refreshRateConfigs, now); // Skip NoVote layer as those don't have any requirements if (vote.type == LayerHistory::LayerVoteType::NoVote) { continue; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index 82f6c3907b..92236f560a 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -42,7 +42,7 @@ class LayerHistory { public: using LayerVoteType = RefreshRateConfigs::LayerVoteType; - LayerHistory(const RefreshRateConfigs&); + LayerHistory(); ~LayerHistory(); // Layers are unregistered when the weak reference expires. @@ -67,7 +67,7 @@ public: using Summary = std::vector<RefreshRateConfigs::LayerRequirement>; // Rebuilds sets of active/inactive layers, and accumulates stats for active layers. - Summary summarize(nsecs_t now); + Summary summarize(const RefreshRateConfigs&, nsecs_t now); void clear(); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 989bf4ef19..8a45b66b60 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -34,7 +34,6 @@ namespace android::scheduler { -const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr; bool LayerInfo::sTraceEnabled = false; LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid, @@ -184,7 +183,8 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { return static_cast<nsecs_t>(averageFrameTime); } -std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) { +std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible( + const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) { static constexpr float MARGIN = 1.0f; // 1Hz if (!hasEnoughDataForHeuristic()) { ALOGV("Not enough data"); @@ -196,9 +196,7 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) { const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime); const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now); if (refreshRateConsistent) { - const auto knownRefreshRate = - sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate); - + const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate); // To avoid oscillation, use the last calculated refresh rate if it is // close enough if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) > @@ -220,7 +218,8 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) { : std::nullopt; } -LayerInfo::LayerVote LayerInfo::getRefreshRateVote(nsecs_t now) { +LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs, + nsecs_t now) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); return mLayerVote; @@ -247,7 +246,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(nsecs_t now) { clearHistory(now); } - auto refreshRate = calculateRefreshRateIfPossible(now); + auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str()); return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 34cc3890d3..ce9783c5a9 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -122,10 +122,6 @@ public: static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; } - static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) { - sRefreshRateConfigs = &refreshRateConfigs; - } - LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote); LayerInfo(const LayerInfo&) = delete; @@ -161,7 +157,7 @@ public: uid_t getOwnerUid() const { return mOwnerUid; } - LayerVote getRefreshRateVote(nsecs_t now); + LayerVote getRefreshRateVote(const RefreshRateConfigs&, nsecs_t now); // Return the last updated time. If the present time is farther in the future than the // updated time, the updated time is the present time. @@ -263,7 +259,7 @@ private: bool isFrequent(nsecs_t now) const; bool isAnimating(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; - std::optional<Fps> calculateRefreshRateIfPossible(nsecs_t now); + std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateConfigs&, nsecs_t now); std::optional<nsecs_t> calculateAverageFrameTime() const; bool isFrameTimeValid(const FrameTimeData&) const; @@ -300,7 +296,6 @@ private: mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags; // Shared for all LayerInfo instances - static const RefreshRateConfigs* sRefreshRateConfigs; static bool sTraceEnabled; }; diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 71cd52d0db..a56827e8f1 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -22,6 +22,7 @@ #pragma clang diagnostic ignored "-Wextra" #include "RefreshRateConfigs.h" +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <utils/Trace.h> #include <chrono> @@ -113,7 +114,7 @@ bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, case LayerVoteType::ExplicitExactOrMultiple: case LayerVoteType::Heuristic: if (mConfig.frameRateMultipleThreshold != 0 && - refreshRate.fps.greaterThanOrEqualWithMargin( + refreshRate.getFps().greaterThanOrEqualWithMargin( Fps(mConfig.frameRateMultipleThreshold)) && layer.desiredRefreshRate.lessThanWithMargin( Fps(mConfig.frameRateMultipleThreshold / 2))) { @@ -491,7 +492,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( } }(); if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && - bestRefreshRate->fps.lessThanWithMargin(touchRefreshRate.fps)) { + bestRefreshRate->getFps().lessThanWithMargin(touchRefreshRate.getFps())) { setTouchConsidered(); ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); return touchRefreshRate; @@ -707,9 +708,30 @@ void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) { RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId, Config config) : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) { + initializeIdleTimer(); updateDisplayModes(modes, currentModeId); } +void RefreshRateConfigs::initializeIdleTimer() { + if (mConfig.idleTimerTimeoutMs > 0) { + const auto getCallback = [this]() -> std::optional<IdleTimerCallbacks::Callbacks> { + std::scoped_lock lock(mIdleTimerCallbacksMutex); + if (!mIdleTimerCallbacks.has_value()) return {}; + return mConfig.supportKernelIdleTimer ? mIdleTimerCallbacks->kernel + : mIdleTimerCallbacks->platform; + }; + + mIdleTimer.emplace( + "IdleTimer", std::chrono::milliseconds(mConfig.idleTimerTimeoutMs), + [getCallback] { + if (const auto callback = getCallback()) callback->onReset(); + }, + [getCallback] { + if (const auto callback = getCallback()) callback->onExpired(); + }); + } +} + void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes, DisplayModeId currentModeId) { std::lock_guard lock(mLock); @@ -727,8 +749,7 @@ void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes, for (const auto& mode : modes) { const auto modeId = mode->getId(); mRefreshRates.emplace(modeId, - std::make_unique<RefreshRate>(modeId, mode, mode->getFps(), - RefreshRate::ConstructorTag(0))); + std::make_unique<RefreshRate>(mode, RefreshRate::ConstructorTag(0))); if (modeId == currentModeId) { mCurrentRefreshRate = mRefreshRates.at(modeId).get(); } @@ -821,7 +842,7 @@ RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const { bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const { std::lock_guard lock(mLock); for (const RefreshRate* refreshRate : mAppRequestRefreshRates) { - if (refreshRate->modeId == modeId) { + if (refreshRate->getModeId() == modeId) { return true; } } @@ -836,7 +857,7 @@ void RefreshRateConfigs::getSortedRefreshRateListLocked( for (const auto& [type, refreshRate] : mRefreshRates) { if (shouldAddRefreshRate(*refreshRate)) { ALOGV("getSortedRefreshRateListLocked: mode %d added to list policy", - refreshRate->modeId.value()); + refreshRate->getModeId().value()); outRefreshRates->push_back(refreshRate.get()); } } @@ -984,6 +1005,9 @@ void RefreshRateConfigs::dump(std::string& result) const { base::StringAppendF(&result, "Supports Frame Rate Override: %s\n", mSupportsFrameRateOverride ? "yes" : "no"); + base::StringAppendF(&result, "Idle timer: (%s) %s\n", + mConfig.supportKernelIdleTimer ? "kernel" : "platform", + mIdleTimer ? mIdleTimer->dump().c_str() : "off"); result.append("\n"); } diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 913e5ee2dc..99f217c2e2 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -27,6 +27,7 @@ #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/HWComposer.h" #include "Fps.h" +#include "Scheduler/OneShotTimer.h" #include "Scheduler/SchedulerUtils.h" #include "Scheduler/Seamlessness.h" #include "Scheduler/StrongTyping.h" @@ -64,25 +65,23 @@ public: }; public: - RefreshRate(DisplayModeId modeId, DisplayModePtr mode, Fps fps, ConstructorTag) - : modeId(modeId), mode(mode), fps(std::move(fps)) {} + RefreshRate(DisplayModePtr mode, ConstructorTag) : mode(mode) {} - DisplayModeId getModeId() const { return modeId; } + DisplayModeId getModeId() const { return mode->getId(); } nsecs_t getVsyncPeriod() const { return mode->getVsyncPeriod(); } int32_t getModeGroup() const { return mode->getGroup(); } - std::string getName() const { return to_string(fps); } - Fps getFps() const { return fps; } + std::string getName() const { return to_string(getFps()); } + Fps getFps() const { return mode->getFps(); } + DisplayModePtr getMode() const { return mode; } // Checks whether the fps of this RefreshRate struct is within a given min and max refresh // rate passed in. Margin of error is applied to the boundaries for approximation. bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const { - return minRefreshRate.lessThanOrEqualWithMargin(fps) && - fps.lessThanOrEqualWithMargin(maxRefreshRate); + return minRefreshRate.lessThanOrEqualWithMargin(getFps()) && + getFps().lessThanOrEqualWithMargin(maxRefreshRate); } - bool operator!=(const RefreshRate& other) const { - return modeId != other.modeId || mode != other.mode; - } + bool operator!=(const RefreshRate& other) const { return mode != other.mode; } bool operator<(const RefreshRate& other) const { return getFps().getValue() < other.getFps().getValue(); @@ -99,10 +98,7 @@ public: friend RefreshRateConfigs; friend class RefreshRateConfigsTest; - const DisplayModeId modeId; DisplayModePtr mode; - // Refresh rate in frames per second - const Fps fps{0.0f}; }; using AllRefreshRatesMapType = @@ -310,13 +306,19 @@ public: // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if // no threshold is set. int frameRateMultipleThreshold = 0; + + // The Idle Timer timeout. 0 timeout means no idle timer. + int32_t idleTimerTimeoutMs = 0; + + // Whether to use idle timer callbacks that support the kernel timer. + bool supportKernelIdleTimer = false; }; - RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId, + RefreshRateConfigs(const DisplayModes&, DisplayModeId, Config config = {.enableFrameRateOverride = false, - .frameRateMultipleThreshold = 0}); - - void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock); + .frameRateMultipleThreshold = 0, + .idleTimerTimeoutMs = 0, + .supportKernelIdleTimer = false}); // Returns whether switching modes (refresh rate or resolution) is possible. // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only @@ -356,8 +358,47 @@ public: Fps displayFrameRate, bool touch) const EXCLUDES(mLock); + bool supportsKernelIdleTimer() const { return mConfig.supportKernelIdleTimer; } + + void setIdleTimerCallbacks(std::function<void()> platformTimerReset, + std::function<void()> platformTimerExpired, + std::function<void()> kernelTimerReset, + std::function<void()> kernelTimerExpired) { + std::scoped_lock lock(mIdleTimerCallbacksMutex); + mIdleTimerCallbacks.emplace(); + mIdleTimerCallbacks->platform.onReset = std::move(platformTimerReset); + mIdleTimerCallbacks->platform.onExpired = std::move(platformTimerExpired); + mIdleTimerCallbacks->kernel.onReset = std::move(kernelTimerReset); + mIdleTimerCallbacks->kernel.onExpired = std::move(kernelTimerExpired); + } + + void startIdleTimer() { + if (mIdleTimer) { + mIdleTimer->start(); + } + } + + void stopIdleTimer() { + if (mIdleTimer) { + mIdleTimer->stop(); + } + } + + void resetIdleTimer(bool kernelOnly) { + if (!mIdleTimer) { + return; + } + if (kernelOnly && !mConfig.supportKernelIdleTimer) { + return; + } + mIdleTimer->reset(); + }; + void dump(std::string& result) const EXCLUDES(mLock); + RefreshRateConfigs(const RefreshRateConfigs&) = delete; + void operator=(const RefreshRateConfigs&) = delete; + private: friend class RefreshRateConfigsTest; @@ -412,6 +453,10 @@ private: float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, const RefreshRate&) const REQUIRES(mLock); + void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock); + + void initializeIdleTimer(); + // The list of refresh rates, indexed by display modes ID. This may change after this // object is initialized. AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock); @@ -455,6 +500,22 @@ private: }; mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation GUARDED_BY(mLock); + + // Timer that records time between requests for next vsync. + std::optional<scheduler::OneShotTimer> mIdleTimer; + + struct IdleTimerCallbacks { + struct Callbacks { + std::function<void()> onReset; + std::function<void()> onExpired; + }; + + Callbacks platform; + Callbacks kernel; + }; + + std::mutex mIdleTimerCallbacksMutex; + std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex); }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 64ad178b26..1989d57cd7 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -25,7 +25,7 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> -#include <input/InputWindow.h> +#include <gui/WindowInfo.h> #include <system/window.h> #include <ui/DisplayStatInfo.h> #include <utils/Timers.h> @@ -64,6 +64,8 @@ using namespace std::string_literals; namespace android { +using gui::WindowInfo; + namespace { std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() { @@ -115,30 +117,18 @@ private: } }; -Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) +Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs, + ISchedulerCallback& callback) : Scheduler(configs, callback, - {.supportKernelTimer = sysprop::support_kernel_idle_timer(false), - .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) { + {.useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) { } -Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback, - Options options) - : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback, - createLayerHistory(configs), options) { +Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs, + ISchedulerCallback& callback, Options options) + : Scheduler(createVsyncSchedule(configs->supportsKernelIdleTimer()), configs, callback, + createLayerHistory(), options) { using namespace sysprop; - const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0); - - if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) { - const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback - : &Scheduler::idleTimerCallback; - mIdleTimer.emplace( - "IdleTimer", std::chrono::milliseconds(millis), - [this, callback] { std::invoke(callback, this, TimerState::Reset); }, - [this, callback] { std::invoke(callback, this, TimerState::Expired); }); - mIdleTimer->start(); - } - if (const int64_t millis = set_touch_timer_ms(0); millis > 0) { // Touch events are coming to SF every 100ms, so the timer needs to be higher than that mTouchTimer.emplace( @@ -157,18 +147,19 @@ Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCal } } -Scheduler::Scheduler(VsyncSchedule schedule, const scheduler::RefreshRateConfigs& configs, +Scheduler::Scheduler(VsyncSchedule schedule, + const std::shared_ptr<scheduler::RefreshRateConfigs>& configs, ISchedulerCallback& schedulerCallback, std::unique_ptr<LayerHistory> layerHistory, Options options) : mOptions(options), mVsyncSchedule(std::move(schedule)), mLayerHistory(std::move(layerHistory)), mSchedulerCallback(schedulerCallback), - mRefreshRateConfigs(configs), mPredictedVsyncTracer( base::GetBoolProperty("debug.sf.show_predicted_vsync", false) ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch) : nullptr) { + setRefreshRateConfigs(configs); mSchedulerCallback.setVsyncEnabled(false); } @@ -176,7 +167,7 @@ Scheduler::~Scheduler() { // Ensure the OneShotTimer threads are joined before we start destroying state. mDisplayPowerTimer.reset(); mTouchTimer.reset(); - mIdleTimer.reset(); + mRefreshRateConfigs.reset(); } Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) { @@ -192,9 +183,8 @@ Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) return {std::move(controller), std::move(tracker), std::move(dispatch)}; } -std::unique_ptr<LayerHistory> Scheduler::createLayerHistory( - const scheduler::RefreshRateConfigs& configs) { - return std::make_unique<scheduler::LayerHistory>(configs); +std::unique_ptr<LayerHistory> Scheduler::createLayerHistory() { + return std::make_unique<scheduler::LayerHistory>(); } std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( @@ -205,11 +195,14 @@ std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( } std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { - if (!mRefreshRateConfigs.supportsFrameRateOverride()) { - return std::nullopt; + { + std::scoped_lock lock(mRefreshRateConfigsLock); + if (!mRefreshRateConfigs->supportsFrameRateOverride()) { + return std::nullopt; + } } - std::lock_guard lock(mFrameRateOverridesMutex); + std::lock_guard lock(mFrameRateOverridesLock); { const auto iter = mFrameRateOverridesFromBackdoor.find(uid); if (iter != mFrameRateOverridesFromBackdoor.end()) { @@ -237,7 +230,8 @@ bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const { } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { - if (!mRefreshRateConfigs.supportsFrameRateOverride()) { + std::scoped_lock lock(mRefreshRateConfigsLock); + if (!mRefreshRateConfigs->supportsFrameRateOverride()) { return {}; } @@ -248,14 +242,18 @@ impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const { return [this](uid_t uid) { - nsecs_t basePeriod = mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod(); + const auto refreshRateConfigs = holdRefreshRateConfigs(); + nsecs_t basePeriod = refreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); const auto frameRate = getFrameRateOverride(uid); if (!frameRate.has_value()) { return basePeriod; } - const auto divider = scheduler::RefreshRateConfigs::getFrameRateDivider( - mRefreshRateConfigs.getCurrentRefreshRate().getFps(), *frameRate); + const auto divider = + scheduler::RefreshRateConfigs::getFrameRateDivider(refreshRateConfigs + ->getCurrentRefreshRate() + .getFps(), + *frameRate); if (divider <= 1) { return basePeriod; } @@ -326,6 +324,7 @@ void Scheduler::onScreenAcquired(ConnectionHandle handle) { thread = mConnections[handle].thread.get(); } thread->onScreenAcquired(); + mScreenAcquired = true; } void Scheduler::onScreenReleased(ConnectionHandle handle) { @@ -336,12 +335,13 @@ void Scheduler::onScreenReleased(ConnectionHandle handle) { thread = mConnections[handle].thread.get(); } thread->onScreenReleased(); + mScreenAcquired = false; } void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) { std::vector<FrameRateOverride> overrides; { - std::lock_guard lock(mFrameRateOverridesMutex); + std::lock_guard lock(mFrameRateOverridesLock); for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) { overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()}); } @@ -360,23 +360,22 @@ void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDis thread->onFrameRateOverridesChanged(displayId, std::move(overrides)); } -void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId, - DisplayModeId modeId, nsecs_t vsyncPeriod) { +void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { { std::lock_guard<std::mutex> lock(mFeatureStateLock); // Cache the last reported modes for primary display. - mFeatures.cachedModeChangedParams = {handle, displayId, modeId, vsyncPeriod}; + mFeatures.cachedModeChangedParams = {handle, mode}; // Invalidate content based refresh rate selection so it could be calculated // again for the new refresh rate. mFeatures.contentRequirements.clear(); } - onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod); + onNonPrimaryDisplayModeChanged(handle, mode); } void Scheduler::dispatchCachedReportedMode() { // Check optional fields first. - if (!mFeatures.modeId.has_value()) { + if (!mFeatures.mode) { ALOGW("No mode ID found, not dispatching cached mode."); return; } @@ -385,39 +384,32 @@ void Scheduler::dispatchCachedReportedMode() { return; } - const auto modeId = *mFeatures.modeId; - // If the modeId is not the current mode, this means that a + // If the mode is not the current mode, this means that a // mode change is in progress. In that case we shouldn't dispatch an event // as it will be dispatched when the current mode changes. - if (mRefreshRateConfigs.getCurrentRefreshRate().getModeId() != modeId) { + if (std::scoped_lock lock(mRefreshRateConfigsLock); + mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mFeatures.mode) { return; } - const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromModeId(modeId).getVsyncPeriod(); - // If there is no change from cached mode, there is no need to dispatch an event - if (modeId == mFeatures.cachedModeChangedParams->modeId && - vsyncPeriod == mFeatures.cachedModeChangedParams->vsyncPeriod) { + if (mFeatures.mode == mFeatures.cachedModeChangedParams->mode) { return; } - mFeatures.cachedModeChangedParams->modeId = modeId; - mFeatures.cachedModeChangedParams->vsyncPeriod = vsyncPeriod; + mFeatures.cachedModeChangedParams->mode = mFeatures.mode; onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle, - mFeatures.cachedModeChangedParams->displayId, - mFeatures.cachedModeChangedParams->modeId, - mFeatures.cachedModeChangedParams->vsyncPeriod); + mFeatures.cachedModeChangedParams->mode); } -void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId, - DisplayModeId modeId, nsecs_t vsyncPeriod) { +void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { android::EventThread* thread; { std::lock_guard<std::mutex> lock(mConnectionsLock); RETURN_IF_INVALID_HANDLE(handle); thread = mConnections[handle].thread.get(); } - thread->onModeChanged(displayId, modeId, vsyncPeriod); + thread->onModeChanged(mode); } size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) { @@ -537,7 +529,11 @@ void Scheduler::resync() { const nsecs_t last = mLastResyncTime.exchange(now); if (now - last > kIgnoreDelay) { - resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod()); + const auto vsyncPeriod = [&] { + std::scoped_lock lock(mRefreshRateConfigsLock); + return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); + }(); + resyncToHardwareVsync(false, vsyncPeriod); } } @@ -586,10 +582,9 @@ void Scheduler::setIgnorePresentFences(bool ignore) { void Scheduler::registerLayer(Layer* layer) { scheduler::LayerHistory::LayerVoteType voteType; - if (!mOptions.useContentDetection || - layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) { + if (!mOptions.useContentDetection || layer->getWindowType() == WindowInfo::Type::STATUS_BAR) { voteType = scheduler::LayerHistory::LayerVoteType::NoVote; - } else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) { + } else if (layer->getWindowType() == WindowInfo::Type::WALLPAPER) { // Running Wallpaper at Min is considered as part of content detection. voteType = scheduler::LayerHistory::LayerVoteType::Min; } else { @@ -608,9 +603,12 @@ void Scheduler::deregisterLayer(Layer* layer) { void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) { - if (mRefreshRateConfigs.canSwitch()) { - mLayerHistory->record(layer, presentTime, systemTime(), updateType); + { + std::scoped_lock lock(mRefreshRateConfigsLock); + if (!mRefreshRateConfigs->canSwitch()) return; } + + mLayerHistory->record(layer, presentTime, systemTime(), updateType); } void Scheduler::setModeChangePending(bool pending) { @@ -618,25 +616,28 @@ void Scheduler::setModeChangePending(bool pending) { } void Scheduler::chooseRefreshRateForContent() { - if (!mRefreshRateConfigs.canSwitch()) return; + { + std::scoped_lock lock(mRefreshRateConfigsLock); + if (!mRefreshRateConfigs->canSwitch()) return; + } ATRACE_CALL(); - scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime()); + const auto refreshRateConfigs = holdRefreshRateConfigs(); + scheduler::LayerHistory::Summary summary = + mLayerHistory->summarize(*refreshRateConfigs, systemTime()); scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; - DisplayModeId newModeId; + DisplayModePtr newMode; bool frameRateChanged; bool frameRateOverridesChanged; { std::lock_guard<std::mutex> lock(mFeatureStateLock); mFeatures.contentRequirements = summary; - newModeId = calculateRefreshRateModeId(&consideredSignals); - auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId); - frameRateOverridesChanged = - updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps()); + newMode = calculateRefreshRateModeId(&consideredSignals); + frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps()); - if (mFeatures.modeId == newModeId) { + if (mFeatures.mode == newMode) { // We don't need to change the display mode, but we might need to send an event // about a mode change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { @@ -644,12 +645,12 @@ void Scheduler::chooseRefreshRateForContent() { } frameRateChanged = false; } else { - mFeatures.modeId = newModeId; + mFeatures.mode = newMode; frameRateChanged = true; } } if (frameRateChanged) { - auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId); + auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId()); mSchedulerCallback.changeRefreshRate(newRefreshRate, consideredSignals.idle ? ModeEvent::None : ModeEvent::Changed); @@ -660,18 +661,16 @@ void Scheduler::chooseRefreshRateForContent() { } void Scheduler::resetIdleTimer() { - if (mIdleTimer) { - mIdleTimer->reset(); - } + std::scoped_lock lock(mRefreshRateConfigsLock); + mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false); } void Scheduler::notifyTouchEvent() { if (mTouchTimer) { mTouchTimer->reset(); - if (mOptions.supportKernelTimer && mIdleTimer) { - mIdleTimer->reset(); - } + std::scoped_lock lock(mRefreshRateConfigsLock); + mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true); } } @@ -695,7 +694,11 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate // magic number - const auto& refreshRate = mRefreshRateConfigs.getCurrentRefreshRate(); + const auto refreshRate = [&] { + std::scoped_lock lock(mRefreshRateConfigsLock); + return mRefreshRateConfigs->getCurrentRefreshRate(); + }(); + constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER{65.0f}; if (state == TimerState::Reset && refreshRate.getFps().greaterThanWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) { @@ -739,7 +742,6 @@ void Scheduler::displayPowerTimerCallback(TimerState state) { void Scheduler::dump(std::string& result) const { using base::StringAppendF; - StringAppendF(&result, "+ Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off"); StringAppendF(&result, "+ Touch timer: %s\n", mTouchTimer ? mTouchTimer->dump().c_str() : "off"); StringAppendF(&result, "+ Content detection: %s %s\n\n", @@ -747,7 +749,7 @@ void Scheduler::dump(std::string& result) const { mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)"); { - std::lock_guard lock(mFrameRateOverridesMutex); + std::lock_guard lock(mFrameRateOverridesLock); StringAppendF(&result, "Frame Rate Overrides (backdoor): {"); for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) { StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str()); @@ -760,6 +762,13 @@ void Scheduler::dump(std::string& result) const { } StringAppendF(&result, "}\n"); } + + { + std::lock_guard lock(mHWVsyncLock); + StringAppendF(&result, + "mScreenAcquired=%d mPrimaryHWVsyncEnabled=%d mHWVsyncAvailable=%d\n", + mScreenAcquired.load(), mPrimaryHWVsyncEnabled, mHWVsyncAvailable); + } } void Scheduler::dumpVsync(std::string& s) const { @@ -773,16 +782,17 @@ void Scheduler::dumpVsync(std::string& s) const { bool Scheduler::updateFrameRateOverrides( scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) { - if (!mRefreshRateConfigs.supportsFrameRateOverride()) { + const auto refreshRateConfigs = holdRefreshRateConfigs(); + if (!refreshRateConfigs->supportsFrameRateOverride()) { return false; } if (!consideredSignals.idle) { const auto frameRateOverrides = - mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements, + refreshRateConfigs->getFrameRateOverrides(mFeatures.contentRequirements, displayRefreshRate, consideredSignals.touch); - std::lock_guard lock(mFrameRateOverridesMutex); + std::lock_guard lock(mFrameRateOverridesLock); if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(), frameRateOverrides.begin(), frameRateOverrides.end(), [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) { @@ -797,33 +807,33 @@ bool Scheduler::updateFrameRateOverrides( template <class T> bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { - DisplayModeId newModeId; + DisplayModePtr newMode; bool refreshRateChanged = false; bool frameRateOverridesChanged; scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; + const auto refreshRateConfigs = holdRefreshRateConfigs(); { std::lock_guard<std::mutex> lock(mFeatureStateLock); if (*currentState == newState) { return false; } *currentState = newState; - newModeId = calculateRefreshRateModeId(&consideredSignals); - const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId); - frameRateOverridesChanged = - updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps()); - if (mFeatures.modeId == newModeId) { + newMode = calculateRefreshRateModeId(&consideredSignals); + frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps()); + if (mFeatures.mode == newMode) { // We don't need to change the display mode, but we might need to send an event // about a mode change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { dispatchCachedReportedMode(); } } else { - mFeatures.modeId = newModeId; + mFeatures.mode = newMode; refreshRateChanged = true; } } if (refreshRateChanged) { - const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId); + const RefreshRate& newRefreshRate = + refreshRateConfigs->getRefreshRateFromModeId(newMode->getId()); mSchedulerCallback.changeRefreshRate(newRefreshRate, consideredSignals.idle ? ModeEvent::None @@ -835,35 +845,36 @@ bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { return consideredSignals.touch; } -DisplayModeId Scheduler::calculateRefreshRateModeId( +DisplayModePtr Scheduler::calculateRefreshRateModeId( scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) { ATRACE_CALL(); if (consideredSignals) *consideredSignals = {}; + const auto refreshRateConfigs = holdRefreshRateConfigs(); // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. if (mDisplayPowerTimer && (!mFeatures.isDisplayPowerStateNormal || mFeatures.displayPowerTimer == TimerState::Reset)) { - return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getModeId(); + return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode(); } const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active; - const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired; + const bool idle = mFeatures.idleTimer == TimerState::Expired; - return mRefreshRateConfigs - .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle}, - consideredSignals) - .getModeId(); + return refreshRateConfigs + ->getBestRefreshRate(mFeatures.contentRequirements, + {.touch = touchActive, .idle = idle}, consideredSignals) + .getMode(); } -std::optional<DisplayModeId> Scheduler::getPreferredModeId() { +DisplayModePtr Scheduler::getPreferredDisplayMode() { std::lock_guard<std::mutex> lock(mFeatureStateLock); // Make sure that the default mode ID is first updated, before returned. - if (mFeatures.modeId.has_value()) { - mFeatures.modeId = calculateRefreshRateModeId(); + if (mFeatures.mode) { + mFeatures.mode = calculateRefreshRateModeId(); } - return mFeatures.modeId; + return mFeatures.mode; } void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) { @@ -899,7 +910,7 @@ void Scheduler::onDisplayRefreshed(nsecs_t timestamp) { } } -void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) { +void Scheduler::onActiveDisplayAreaChanged(uint32_t displayArea) { mLayerHistory->setDisplayArea(displayArea); } @@ -908,7 +919,7 @@ void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverrid return; } - std::lock_guard lock(mFrameRateOverridesMutex); + std::lock_guard lock(mFrameRateOverridesLock); if (frameRateOverride.frameRateHz != 0.f) { mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz); } else { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 30a32537ad..bbbbca5b2f 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -72,7 +72,7 @@ public: using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; using ModeEvent = scheduler::RefreshRateConfigEvent; - Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&); + Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&); ~Scheduler(); using ConnectionHandle = scheduler::ConnectionHandle; @@ -87,15 +87,13 @@ public: sp<EventThreadConnection> getEventConnection(ConnectionHandle); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); - void onPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId, - nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock); - void onNonPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId, - nsecs_t vsyncPeriod); + void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mFeatureStateLock); + void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr); void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId) - EXCLUDES(mFrameRateOverridesMutex) EXCLUDES(mConnectionsLock); + EXCLUDES(mFrameRateOverridesLock) EXCLUDES(mConnectionsLock); // Modifies work duration in the event thread. void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, @@ -116,7 +114,7 @@ public: // no-op. // The period is the vsync period from the current display configuration. void resyncToHardwareVsync(bool makeAvailable, nsecs_t period); - void resync(); + void resync() EXCLUDES(mRefreshRateConfigsLock); // Passes a vsync sample to VsyncController. periodFlushed will be true if // VsyncController detected that the vsync period changed, and false otherwise. @@ -127,14 +125,14 @@ public: // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); - void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType); + void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) + EXCLUDES(mRefreshRateConfigsLock); void setModeChangePending(bool pending); void deregisterLayer(Layer*); // Detects content using layer history, and selects a matching refresh rate. - void chooseRefreshRateForContent(); + void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock); - bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); } void resetIdleTimer(); // Function that resets the touch timer. @@ -147,7 +145,7 @@ public: // Returns true if a given vsync timestamp is considered valid vsync // for a given uid bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const - EXCLUDES(mFrameRateOverridesMutex); + EXCLUDES(mFrameRateOverridesLock); std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const; @@ -156,7 +154,7 @@ public: void dumpVsync(std::string&) const; // Get the appropriate refresh for current conditions. - std::optional<DisplayModeId> getPreferredModeId(); + DisplayModePtr getPreferredDisplayMode(); // Notifies the scheduler about a refresh rate timeline change. void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline); @@ -165,7 +163,7 @@ public: void onDisplayRefreshed(nsecs_t timestamp); // Notifies the scheduler when the display size has changed. Called from SF's main thread - void onPrimaryDisplayAreaChanged(uint32_t displayArea); + void onActiveDisplayAreaChanged(uint32_t displayArea); size_t getEventThreadConnectionCount(ConnectionHandle handle); @@ -176,9 +174,45 @@ public: // Stores the preferred refresh rate that an app should run at. // FrameRateOverride.refreshRateHz == 0 means no preference. - void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesMutex); + void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesLock); // Retrieves the overridden refresh rate for a given uid. - std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFrameRateOverridesMutex); + std::optional<Fps> getFrameRateOverride(uid_t uid) const + EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock); + + void setRefreshRateConfigs(std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs) + EXCLUDES(mRefreshRateConfigsLock) { + // We need to stop the idle timer on the previous RefreshRateConfigs instance + // and cleanup the scheduler's state before we switch to the other RefreshRateConfigs. + { + std::scoped_lock lock(mRefreshRateConfigsLock); + if (mRefreshRateConfigs) mRefreshRateConfigs->stopIdleTimer(); + } + { + std::scoped_lock lock(mFeatureStateLock); + mFeatures = {}; + } + { + std::scoped_lock lock(mRefreshRateConfigsLock); + mRefreshRateConfigs = std::move(refreshRateConfigs); + mRefreshRateConfigs->setIdleTimerCallbacks( + [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); }, + [this] { + std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired); + }, + [this] { + std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset); + }, + [this] { + std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired); + }); + mRefreshRateConfigs->startIdleTimer(); + } + } + + nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) { + std::scoped_lock lock(mRefreshRateConfigsLock); + return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); + } private: friend class TestableScheduler; @@ -190,8 +224,6 @@ private: enum class TouchState { Inactive, Active }; struct Options { - // Whether to use idle timer callbacks that support the kernel timer. - bool supportKernelTimer; // Whether to use content detection at all. bool useContentDetection; }; @@ -203,14 +235,14 @@ private: }; // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers. - Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&, Options); + Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&, Options); // Used by tests to inject mocks. - Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&, - std::unique_ptr<LayerHistory>, Options); + Scheduler(VsyncSchedule, const std::shared_ptr<scheduler::RefreshRateConfigs>&, + ISchedulerCallback&, std::unique_ptr<LayerHistory>, Options); static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer); - static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&); + static std::unique_ptr<LayerHistory> createLayerHistory(); // Create a connection on the given EventThread. ConnectionHandle createConnection(std::unique_ptr<EventThread>); @@ -218,7 +250,7 @@ private: EventThread*, ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); // Update feature state machine to given state when corresponding timer resets or expires. - void kernelIdleTimerCallback(TimerState); + void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateConfigsLock); void idleTimerCallback(TimerState); void touchTimerCallback(TimerState); void displayPowerTimerCallback(TimerState); @@ -232,18 +264,25 @@ private: // This function checks whether individual features that are affecting the refresh rate // selection were initialized, prioritizes them, and calculates the DisplayModeId // for the suggested refresh rate. - DisplayModeId calculateRefreshRateModeId( + DisplayModePtr calculateRefreshRateModeId( scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mFeatureStateLock); - void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock); + void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock) EXCLUDES(mRefreshRateConfigsLock); bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) REQUIRES(mFeatureStateLock) - EXCLUDES(mFrameRateOverridesMutex); + EXCLUDES(mFrameRateOverridesLock); - impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const; + impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const + EXCLUDES(mRefreshRateConfigsLock); impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const; + std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const + EXCLUDES(mRefreshRateConfigsLock) { + std::scoped_lock lock(mRefreshRateConfigsLock); + return mRefreshRateConfigs; + } + // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { sp<EventThreadConnection> connection; @@ -258,7 +297,7 @@ private: InjectVSyncSource* mVSyncInjector = nullptr; ConnectionHandle mInjectorConnectionHandle; - std::mutex mHWVsyncLock; + mutable std::mutex mHWVsyncLock; bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false; bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false; @@ -270,8 +309,6 @@ private: // Used to choose refresh rate if content detection is enabled. std::unique_ptr<LayerHistory> mLayerHistory; - // Timer that records time between requests for next vsync. - std::optional<scheduler::OneShotTimer> mIdleTimer; // Timer used to monitor touch events. std::optional<scheduler::OneShotTimer> mTouchTimer; // Timer used to monitor display power mode. @@ -288,7 +325,7 @@ private: TouchState touch = TouchState::Inactive; TimerState displayPowerTimer = TimerState::Expired; - std::optional<DisplayModeId> modeId; + DisplayModePtr mode; LayerHistory::Summary contentRequirements; bool isDisplayPowerStateNormal = true; @@ -296,15 +333,15 @@ private: // Used to cache the last parameters of onPrimaryDisplayModeChanged struct ModeChangedParams { ConnectionHandle handle; - PhysicalDisplayId displayId; - DisplayModeId modeId; - nsecs_t vsyncPeriod; + DisplayModePtr mode; }; std::optional<ModeChangedParams> cachedModeChangedParams; } mFeatures GUARDED_BY(mFeatureStateLock); - const scheduler::RefreshRateConfigs& mRefreshRateConfigs; + mutable std::mutex mRefreshRateConfigsLock; + std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs + GUARDED_BY(mRefreshRateConfigsLock); std::mutex mVsyncTimelineLock; std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline @@ -315,14 +352,17 @@ private: // The frame rate override lists need their own mutex as they are being read // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks - mutable std::mutex mFrameRateOverridesMutex; + mutable std::mutex mFrameRateOverridesLock; // mappings between a UID and a preferred refresh rate that this app would // run at. scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent - GUARDED_BY(mFrameRateOverridesMutex); + GUARDED_BY(mFrameRateOverridesLock); scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor - GUARDED_BY(mFrameRateOverridesMutex); + GUARDED_BY(mFrameRateOverridesLock); + + // Keeps track of whether the screen is acquired for debug + std::atomic<bool> mScreenAcquired = false; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 7b5d4626dc..ee973f718a 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -42,6 +42,7 @@ VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, : mClock(std::move(clock)), mTracker(tracker), mPendingLimit(pendingFenceLimit), + // TODO(adyabr): change mSupportKernelIdleTimer when the active display changes mSupportKernelIdleTimer(supportKernelIdleTimer) {} VSyncReactor::~VSyncReactor() = default; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 893dc2a5d2..ce793ad9c5 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -31,7 +31,6 @@ #include <android/hardware/configstore/1.1/types.h> #include <android/hardware/power/Boost.h> #include <android/native_window.h> -#include <android/os/BnSetInputWindowsListener.h> #include <android/os/IInputFlinger.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -115,6 +114,7 @@ #include "FrameTracer/FrameTracer.h" #include "HdrLayerInfoReporter.h" #include "Layer.h" +#include "LayerProtoHelper.h" #include "LayerRenderArea.h" #include "LayerVector.h" #include "MonitoredProducer.h" @@ -133,6 +133,7 @@ #include "SurfaceInterceptor.h" #include "TimeStats/TimeStats.h" #include "TunnelModeEnabledReporter.h" +#include "WindowInfosListenerInvoker.h" #include "android-base/parseint.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" @@ -146,6 +147,13 @@ return (expr); \ }() +#define MAIN_THREAD_GUARD(expr) \ + [&] { \ + LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \ + MainThreadScopedGuard lock(SF_MAIN_THREAD); \ + return (expr); \ + }() + #undef NO_THREAD_SAFETY_ANALYSIS #define NO_THREAD_SAFETY_ANALYSIS \ _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"") @@ -160,6 +168,8 @@ using namespace android::sysprop; using android::hardware::power::Boost; using base::StringAppendF; +using gui::IWindowInfosListener; +using gui::WindowInfo; using ui::ColorMode; using ui::Dataspace; using ui::DisplayPrimaries; @@ -265,23 +275,42 @@ enum Permission { ROTATE_SURFACE_FLINGER = 0x2, }; -} // namespace anonymous +struct IdleTimerConfig { + int32_t timeoutMs; + bool supportKernelIdleTimer; +}; + +IdleTimerConfig getIdleTimerConfiguration(DisplayId displayId) { + // TODO(adyabr): use ro.surface_flinger.* namespace -struct SetInputWindowsListener : os::BnSetInputWindowsListener { - explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {} + const auto displayIdleTimerMsKey = [displayId] { + std::stringstream ss; + ss << "debug.sf.set_idle_timer_ms_" << displayId.value; + return ss.str(); + }(); - binder::Status onSetInputWindowsFinished() override; + const auto displaySupportKernelIdleTimerKey = [displayId] { + std::stringstream ss; + ss << "debug.sf.support_kernel_idle_timer_" << displayId.value; + return ss.str(); + }(); - std::function<void()> mListenerCb; -}; + const int32_t displayIdleTimerMs = base::GetIntProperty(displayIdleTimerMsKey, 0); + const auto displaySupportKernelIdleTimer = + base::GetBoolProperty(displaySupportKernelIdleTimerKey, false); -binder::Status SetInputWindowsListener::onSetInputWindowsFinished() { - if (mListenerCb != nullptr) { - mListenerCb(); + if (displayIdleTimerMs > 0) { + return {displayIdleTimerMs, displaySupportKernelIdleTimer}; } - return binder::Status::ok(); + + 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)}; } +} // namespace anonymous + // --------------------------------------------------------------------------- const String16 sHardwareTest("android.permission.HARDWARE_TEST"); @@ -347,10 +376,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()), mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)), mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)), - mPowerAdvisor(*this) { + mPowerAdvisor(*this), + mWindowInfosListenerInvoker(new WindowInfosListenerInvoker(this)) { ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str()); - - mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); }); } SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) { @@ -475,9 +503,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI useFrameRateApi = use_frame_rate_api(true); - mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false); - base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false"); - mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0); // Debug property overrides ro. property @@ -590,19 +615,11 @@ void SurfaceFlinger::enableHalVirtualDisplays(bool enable) { } } -VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format, - ui::LayerStack layerStack) { +VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, + ui::PixelFormat format) { if (auto& generator = mVirtualDisplayIdGenerators.hal) { if (const auto id = generator->generateId()) { - std::optional<PhysicalDisplayId> mirror; - - if (const auto display = findDisplay([layerStack](const auto& display) { - return !display.isVirtual() && display.getLayerStack() == layerStack; - })) { - mirror = display->getPhysicalId(); - } - - if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format, mirror)) { + if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) { return *id; } @@ -632,20 +649,18 @@ void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) { mVirtualDisplayIdGenerators.gpu.releaseId(*id); } -std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const { - Mutex::Autolock lock(mStateLock); - - const auto internalDisplayId = getInternalDisplayIdLocked(); - if (!internalDisplayId) { +std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const { + const auto display = getDefaultDisplayDeviceLocked(); + if (!display) { return {}; } std::vector<PhysicalDisplayId> displayIds; displayIds.reserve(mPhysicalDisplayTokens.size()); - displayIds.push_back(*internalDisplayId); + displayIds.push_back(display->getPhysicalId()); for (const auto& [id, token] : mPhysicalDisplayTokens) { - if (id != *internalDisplayId) { + if (id != display->getPhysicalId()) { displayIds.push_back(id); } } @@ -739,7 +754,7 @@ void SurfaceFlinger::bootFinished() { mBootStage = BootStage::FINISHED; if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) { - enableRefreshRateOverlay(true); + ON_MAIN_THREAD(enableRefreshRateOverlay(true)); } })); } @@ -803,6 +818,8 @@ void SurfaceFlinger::init() { ? renderengine::RenderEngine::ContextPriority::REALTIME : renderengine::RenderEngine::ContextPriority::MEDIUM) .build())); + mMaxRenderTargetSize = + std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims()); // Set SF main policy after initializing RenderEngine which has its own policy. if (!SetTaskProfiles(0, {"SFMainPolicy"})) { @@ -848,7 +865,7 @@ void SurfaceFlinger::init() { } } - getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize()); + onActiveDisplaySizeChanged(display); // Inform native graphics APIs whether the present timestamp is supported: @@ -890,14 +907,6 @@ void SurfaceFlinger::startBootAnim() { } } -size_t SurfaceFlinger::getMaxTextureSize() const { - return getRenderEngine().getMaxTextureSize(); -} - -size_t SurfaceFlinger::getMaxViewportDims() const { - return getRenderEngine().getMaxViewportDims(); -} - // ---------------------------------------------------------------------------- bool SurfaceFlinger::authenticateSurfaceTexture( @@ -1086,36 +1095,28 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* st void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) { ATRACE_CALL(); - auto refreshRate = mRefreshRateConfigs->getRefreshRateFromModeId(info.modeId); - ALOGV("%s(%s)", __func__, refreshRate.getName().c_str()); - - std::lock_guard<std::mutex> lock(mActiveModeLock); - if (mDesiredActiveModeChanged) { - // If a mode change is pending, just cache the latest request in mDesiredActiveMode - const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event; - mDesiredActiveMode = info; - mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig; - } else { - // Check if we are already at the desired mode - const auto display = getDefaultDisplayDeviceLocked(); - if (!display || display->getActiveMode()->getId() == refreshRate.getModeId()) { - return; - } - // Initiate a mode change. - mDesiredActiveModeChanged = true; - mDesiredActiveMode = info; + if (!info.mode) { + ALOGW("requested display mode is null"); + return; + } + auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId()); + if (!display) { + ALOGW("%s: display is no longer valid", __func__); + return; + } + if (display->setDesiredActiveMode(info)) { // This will trigger HWC refresh without resetting the idle timer. repaintEverythingForHWC(); // Start receiving vsync samples now, so that we can detect a period // switch. - mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod()); + mScheduler->resyncToHardwareVsync(true, info.mode->getVsyncPeriod()); // As we called to set period, we will call to onRefreshRateChangeCompleted once // VsyncController model is locked. modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated); - updatePhaseConfiguration(refreshRate.getFps()); + updatePhaseConfiguration(info.mode->getFps()); mScheduler->setModeChangePending(true); } } @@ -1149,7 +1150,7 @@ status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int mode const auto fps = mode->getFps(); // Keep the old switching type. const auto allowGroupSwitching = - mRefreshRateConfigs->getCurrentPolicy().allowGroupSwitching; + display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching; const scheduler::RefreshRateConfigs::Policy policy{mode->getId(), allowGroupSwitching, {fps, fps}}; @@ -1169,63 +1170,49 @@ void SurfaceFlinger::setActiveModeInternal() { return; } - const auto upcomingMode = display->getMode(mUpcomingActiveMode.modeId); - if (!upcomingMode) { - ALOGW("Upcoming active mode is no longer supported. Mode ID = %d", - mUpcomingActiveMode.modeId.value()); - // TODO(b/159590486) Handle the error better. Some parts of SurfaceFlinger may - // have been already updated with the upcoming active mode. + const auto upcomingModeInfo = MAIN_THREAD_GUARD(display->getUpcomingActiveMode()); + if (!upcomingModeInfo.mode) { + // There is no pending mode change. This can happen if the active + // display changed and the mode change happened on a different display. return; } - if (display->getActiveMode()->getSize() != upcomingMode->getSize()) { + if (display->getActiveMode()->getSize() != upcomingModeInfo.mode->getSize()) { auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken()); // We need to generate new sequenceId in order to recreate the display (and this // way the framebuffer). state.sequenceId = DisplayDeviceState{}.sequenceId; - state.physical->activeMode = upcomingMode; + state.physical->activeMode = upcomingModeInfo.mode; processDisplayChangesLocked(); // processDisplayChangesLocked will update all necessary components so we're done here. return; } - std::lock_guard<std::mutex> lock(mActiveModeLock); - mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId); - display->setActiveMode(mUpcomingActiveMode.modeId); - - const Fps refreshRate = upcomingMode->getFps(); + // We just created this display so we can call even if we are not on + // the main thread + MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD); + display->setActiveMode(upcomingModeInfo.mode->getId()); + const Fps refreshRate = upcomingModeInfo.mode->getFps(); mRefreshRateStats->setRefreshRate(refreshRate); - updatePhaseConfiguration(refreshRate); - ATRACE_INT("ActiveConfigFPS", refreshRate.getValue()); - if (mRefreshRateOverlay) { - mRefreshRateOverlay->changeRefreshRate(upcomingMode->getFps()); - } - - if (mUpcomingActiveMode.event != Scheduler::ModeEvent::None) { - const nsecs_t vsyncPeriod = refreshRate.getPeriodNsecs(); - const auto physicalId = display->getPhysicalId(); - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId, - mUpcomingActiveMode.modeId, vsyncPeriod); + if (upcomingModeInfo.event != Scheduler::ModeEvent::None) { + mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode); } } -void SurfaceFlinger::clearDesiredActiveModeState() { - std::lock_guard<std::mutex> lock(mActiveModeLock); - mDesiredActiveMode.event = Scheduler::ModeEvent::None; - mDesiredActiveModeChanged = false; - mScheduler->setModeChangePending(false); +void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) { + display->clearDesiredActiveModeState(); + if (isDisplayActiveLocked(display)) { + mScheduler->setModeChangePending(false); + } } -void SurfaceFlinger::desiredActiveModeChangeDone() { - const auto modeId = getDesiredActiveMode()->modeId; - - clearDesiredActiveModeState(); - - const auto refreshRate = getDefaultDisplayDeviceLocked()->getMode(modeId)->getFps(); +void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) { + const auto refreshRate = display->getDesiredActiveMode()->mode->getFps(); + clearDesiredActiveModeState(display); mScheduler->resyncToHardwareVsync(true, refreshRate.getPeriodNsecs()); updatePhaseConfiguration(refreshRate); } @@ -1233,63 +1220,75 @@ void SurfaceFlinger::desiredActiveModeChangeDone() { void SurfaceFlinger::performSetActiveMode() { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); - // Store the local variable to release the lock. - const auto desiredActiveMode = getDesiredActiveMode(); - if (!desiredActiveMode) { - // No desired active mode pending to be applied - return; - } - const auto display = getDefaultDisplayDeviceLocked(); - const auto desiredMode = display->getMode(desiredActiveMode->modeId); - if (!desiredMode) { - ALOGW("Desired display mode is no longer supported. Mode ID = %d", - desiredActiveMode->modeId.value()); - clearDesiredActiveModeState(); - return; - } - const auto refreshRate = desiredMode->getFps(); - ALOGV("%s changing active mode to %d(%s)", __FUNCTION__, desiredMode->getId().value(), - to_string(refreshRate).c_str()); + for (const auto& iter : mDisplays) { + const auto& display = iter.second; + if (!display || !display->isInternal()) { + continue; + } - if (!display || display->getActiveMode()->getId() == desiredActiveMode->modeId) { - // display is not valid or we are already in the requested mode - // on both cases there is nothing left to do - desiredActiveModeChangeDone(); - return; - } + // Store the local variable to release the lock. + const auto desiredActiveMode = display->getDesiredActiveMode(); + if (!desiredActiveMode) { + // No desired active mode pending to be applied + continue; + } - // Desired active mode was set, it is different than the mode currently in use, however - // allowed modes might have changed by the time we process the refresh. - // Make sure the desired mode is still allowed - if (!isDisplayModeAllowed(desiredActiveMode->modeId)) { - desiredActiveModeChangeDone(); - return; - } + if (!isDisplayActiveLocked(display)) { + // display is no longer the active display, so abort the mode change + clearDesiredActiveModeState(display); + continue; + } - mUpcomingActiveMode = *desiredActiveMode; + const auto desiredMode = display->getMode(desiredActiveMode->mode->getId()); + if (!desiredMode) { + ALOGW("Desired display mode is no longer supported. Mode ID = %d", + desiredActiveMode->mode->getId().value()); + clearDesiredActiveModeState(display); + continue; + } - ATRACE_INT("ActiveModeFPS_HWC", refreshRate.getValue()); + const auto refreshRate = desiredMode->getFps(); + ALOGV("%s changing active mode to %d(%s) for display %s", __func__, + desiredMode->getId().value(), to_string(refreshRate).c_str(), + to_string(display->getId()).c_str()); - // TODO(b/142753666) use constrains - hal::VsyncPeriodChangeConstraints constraints; - constraints.desiredTimeNanos = systemTime(); - constraints.seamlessRequired = false; + if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) { + // display is not valid or we are already in the requested mode + // on both cases there is nothing left to do + desiredActiveModeChangeDone(display); + continue; + } - hal::VsyncPeriodChangeTimeline outTimeline; - const auto status = - display->initiateModeChange(mUpcomingActiveMode.modeId, constraints, &outTimeline); - if (status != NO_ERROR) { - // initiateModeChange may fail if a hotplug event is just about - // to be sent. We just log the error in this case. - ALOGW("initiateModeChange failed: %d", status); - return; - } + // Desired active mode was set, it is different than the mode currently in use, however + // allowed modes might have changed by the time we process the refresh. + // Make sure the desired mode is still allowed + const auto displayModeAllowed = + display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId()); + if (!displayModeAllowed) { + desiredActiveModeChangeDone(display); + continue; + } - mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline); + // TODO(b/142753666) use constrains + hal::VsyncPeriodChangeConstraints constraints; + constraints.desiredTimeNanos = systemTime(); + constraints.seamlessRequired = false; + hal::VsyncPeriodChangeTimeline outTimeline; + + const auto status = MAIN_THREAD_GUARD( + display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline)); + if (status != NO_ERROR) { + // initiateModeChange may fail if a hotplug event is just about + // to be sent. We just log the error in this case. + ALOGW("initiateModeChange failed: %d", status); + continue; + } + mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline); - // Scheduler will submit an empty frame to HWC if needed. - mSetActiveModePending = true; + // Scheduler will submit an empty frame to HWC if needed. + mSetActiveModePending = true; + } } void SurfaceFlinger::disableExpensiveRendering() { @@ -1731,10 +1730,10 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t ATRACE_CALL(); Mutex::Autolock lock(mStateLock); - - if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId)) { - auto token = getPhysicalDisplayTokenLocked(*displayId); - auto display = getDisplayDeviceLocked(token); + const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId); + if (displayId) { + const auto token = getPhysicalDisplayTokenLocked(*displayId); + const auto display = getDisplayDeviceLocked(token); display->onVsync(timestamp); } @@ -1742,8 +1741,10 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t return; } - if (hwcDisplayId != getHwComposer().getInternalHwcDisplayId()) { - // For now, we don't do anything with external display vsyncs. + const bool isActiveDisplay = + displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken; + if (!isActiveDisplay) { + // For now, we don't do anything with non active display vsyncs. return; } @@ -1759,10 +1760,6 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { *compositorTiming = getBE().mCompositorTiming; } -bool SurfaceFlinger::isDisplayModeAllowed(DisplayModeId modeId) const { - return mRefreshRateConfigs->isModeAllowed(modeId); -} - void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate, Scheduler::ModeEvent event) { const auto display = getDefaultDisplayDeviceLocked(); @@ -1772,13 +1769,13 @@ void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate, ATRACE_CALL(); // Don't do any updating if the current fps is the same as the new one. - if (!isDisplayModeAllowed(refreshRate.getModeId())) { + if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) { ALOGV("Skipping mode %d as it is not part of allowed modes", refreshRate.getModeId().value()); return; } - setDesiredActiveMode({refreshRate.getModeId(), event}); + setDesiredActiveMode({refreshRate.getMode(), event}); } void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, @@ -1827,7 +1824,7 @@ void SurfaceFlinger::setVsyncEnabled(bool enabled) { if (const auto display = getDefaultDisplayDeviceLocked(); display && display->isPoweredOn()) { - getHwComposer().setVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState); + setHWCVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState); } })); } @@ -1979,8 +1976,13 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT } if (mRefreshRateOverlaySpinner) { - if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) { - mRefreshRateOverlay->onInvalidate(); + if (Mutex::Autolock lock(mStateLock); + const auto display = getDefaultDisplayDeviceLocked()) { + if (display) { + display->onInvalidate(); + } else { + ALOGW("%s: default display is null", __func__); + } } } @@ -2366,7 +2368,7 @@ void SurfaceFlinger::postComposition() { mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence); mTransactionCallbackInvoker.sendCallbacks(); - if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON && + if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON && mPreviousPresentFences[0].fenceTime->isValid()) { mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime); } @@ -2457,24 +2459,34 @@ void SurfaceFlinger::postComposition() { } } -FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const { - return displayDevice.getLayerStackSpaceRect().toFloatRect(); -} - -void SurfaceFlinger::computeLayerBounds() { +FloatRect SurfaceFlinger::getMaxDisplayBounds() { + // Find the largest width and height among all the displays. + int32_t maxDisplayWidth = 0; + int32_t maxDisplayHeight = 0; for (const auto& pair : ON_MAIN_THREAD(mDisplays)) { const auto& displayDevice = pair.second; - const auto display = displayDevice->getCompositionDisplay(); - for (const auto& layer : mDrawingState.layersSortedByZ) { - // only consider the layers on the given layer stack - if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) { - continue; - } - - layer->computeBounds(getLayerClipBoundsForDisplay(*displayDevice), ui::Transform(), - 0.f /* shadowRadius */); + int32_t width = displayDevice->getWidth(); + int32_t height = displayDevice->getHeight(); + if (width > maxDisplayWidth) { + maxDisplayWidth = width; + } + if (height > maxDisplayHeight) { + maxDisplayHeight = height; } } + + // Ignore display bounds for now since they will be computed later. Use a large Rect bound + // to ensure it's bigger than an actual display will be. + FloatRect maxBounds = FloatRect(-maxDisplayWidth * 10, -maxDisplayHeight * 10, + maxDisplayWidth * 10, maxDisplayHeight * 10); + return maxBounds; +} + +void SurfaceFlinger::computeLayerBounds() { + FloatRect maxBounds = getMaxDisplayBounds(); + for (const auto& layer : mDrawingState.layersSortedByZ) { + layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */); + } } void SurfaceFlinger::postFrame() { @@ -2504,7 +2516,6 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { // We call getTransactionFlags(), which will also clear the flags, // with mStateLock held to guarantee that mCurrentState won't change // until the transaction is committed. - modulateVsync(&VsyncModulator::onTransactionCommit); transactionFlags = getTransactionFlags(eTransactionMask); handleTransactionLocked(transactionFlags); @@ -2555,6 +2566,7 @@ void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& for (const auto& hwcMode : hwcModes) { newModes.push_back(DisplayMode::Builder(hwcMode.hwcId) .setId(DisplayModeId{nextModeId++}) + .setPhysicalDisplayId(displayId) .setWidth(hwcMode.width) .setHeight(hwcMode.height) .setVsyncPeriod(hwcMode.vsyncPeriod) @@ -2616,11 +2628,6 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { sp<IBinder> token = new BBinder(); mCurrentState.displays.add(token, state); mPhysicalDisplayTokens.emplace(displayId, std::move(token)); - - if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) { - initScheduler(state); - } - mInterceptor->saveDisplayCreation(state); } else { ALOGV("Recreating display %s", to_string(displayId).c_str()); @@ -2675,6 +2682,18 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( if (const auto& physical = state.physical) { creationArgs.connectionType = physical->type; creationArgs.supportedModes = physical->supportedModes; + creationArgs.activeModeId = physical->activeMode->getId(); + const auto [idleTimerTimeoutMs, supportKernelIdleTimer] = + getIdleTimerConfiguration(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}; + creationArgs.refreshRateConfigs = + std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes, + creationArgs.activeModeId, config); } if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) { @@ -2731,7 +2750,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN}); if (!state.isVirtual()) { - display->setActiveMode(state.physical->activeMode->getId()); + MAIN_THREAD_GUARD(display->setActiveMode(state.physical->activeMode->getId())); display->setDeviceProductInfo(state.physical->deviceProductInfo); } @@ -2771,7 +2790,7 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, builder.setId(physical->id); builder.setConnectionType(physical->type); } else { - builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.layerStack)); + builder.setId(acquireVirtualDisplay(resolution, pixelFormat)); } builder.setPixels(resolution); @@ -2813,14 +2832,12 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, const auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay), state, displaySurface, producer); mDisplays.emplace(displayToken, display); + if (display->isPrimary()) { + initScheduler(display); + } if (!state.isVirtual()) { dispatchDisplayHotplugEvent(display->getPhysicalId(), true); } - - if (display->isPrimary()) { - mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight()); - getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize()); - } } void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { @@ -2885,16 +2902,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, // TODO(b/175678251) Call a listener instead. if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) { - mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes, - currentState.physical->activeMode->getId()); - mVsyncConfiguration->reset(); - const Fps refreshRate = currentState.physical->activeMode->getFps(); - updatePhaseConfiguration(refreshRate); - mRefreshRateStats->setRefreshRate(refreshRate); - - if (mRefreshRateOverlay) { - mRefreshRateOverlay->reset(); - } + updateInternalDisplayVsyncLocked(display); } } return; @@ -2904,29 +2912,34 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (currentState.layerStack != drawingState.layerStack) { display->setLayerStack(currentState.layerStack); } + if (currentState.flags != drawingState.flags) { + display->setFlags(currentState.flags); + } if ((currentState.orientation != drawingState.orientation) || (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) || (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) { display->setProjection(currentState.orientation, currentState.layerStackSpaceRect, currentState.orientedDisplaySpaceRect); - if (display->isPrimary()) { - mDefaultDisplayTransformHint = display->getTransformHint(); + if (isDisplayActiveLocked(display)) { + mActiveDisplayTransformHint = display->getTransformHint(); } } if (currentState.width != drawingState.width || currentState.height != drawingState.height) { display->setDisplaySize(currentState.width, currentState.height); - if (display->isPrimary()) { - mScheduler->onPrimaryDisplayAreaChanged(currentState.width * currentState.height); - } - - if (mRefreshRateOverlay) { - mRefreshRateOverlay->setViewport(display->getSize()); + if (isDisplayActiveLocked(display)) { + onActiveDisplaySizeChanged(display); } } } } +void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) { + mVsyncConfiguration->reset(); + const Fps refreshRate = activeDisplay->refreshRateConfigs().getCurrentRefreshRate().getFps(); + updatePhaseConfiguration(refreshRate); + mRefreshRateStats->setRefreshRate(refreshRate); +} void SurfaceFlinger::processDisplayChangesLocked() { // here we take advantage of Vector's copy-on-write semantics to @@ -3088,11 +3101,11 @@ void SurfaceFlinger::updateInputFlinger() { if (mVisibleRegionsDirty || mInputInfoChanged) { mInputInfoChanged = false; - updateInputWindowInfo(); + notifyWindowInfos(); } else if (mInputWindowCommands.syncInputWindows) { // If the caller requested to sync input windows, but there are no // changes to input windows, notify immediately. - setInputWindowsFinished(); + windowInfosReported(); } for (const auto& focusRequest : mInputWindowCommands.focusRequests) { @@ -3107,31 +3120,20 @@ bool enablePerWindowInputRotation() { return value; } -void SurfaceFlinger::updateInputWindowInfo() { - std::vector<InputWindowInfo> inputInfos; +void SurfaceFlinger::notifyWindowInfos() { + std::vector<WindowInfo> windowInfos; mDrawingState.traverseInReverseZOrder([&](Layer* layer) { if (!layer->needsInputInfo()) return; - sp<DisplayDevice> display; - if (enablePerWindowInputRotation()) { - for (const auto& pair : ON_MAIN_THREAD(mDisplays)) { - const auto& displayDevice = pair.second; - if (!displayDevice->getCompositionDisplay() - ->belongsInOutput(layer->getLayerStack(), - layer->getPrimaryDisplayOnly())) { - continue; - } - display = displayDevice; - } - } + sp<DisplayDevice> display = enablePerWindowInputRotation() + ? ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)) + : nullptr; // When calculating the screen bounds we ignore the transparent region since it may // result in an unwanted offset. - inputInfos.push_back(layer->fillInputInfo(display)); + windowInfos.push_back(layer->fillInputInfo(display)); }); - - mInputFlinger->setInputWindows(inputInfos, - mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener - : nullptr); + mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, + mInputWindowCommands.syncInputWindows); } void SurfaceFlinger::updateCursorAsync() { @@ -3163,24 +3165,16 @@ void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); } -void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) { +void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { if (mScheduler) { - // In practice it's not allowed to hotplug in/out the primary display once it's been - // connected during startup, but some tests do it, so just warn and return. - ALOGW("Can't re-init scheduler"); + // If the scheduler is already initialized, this means that we received + // a hotplug(connected) on the primary display. In that case we should + // update the scheduler with the most recent display information. + ALOGW("Scheduler already initialized, updating instead"); + mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs()); return; } - const auto displayId = displayState.physical->id; - scheduler::RefreshRateConfigs::Config config = - {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false), - .frameRateMultipleThreshold = - base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0)}; - mRefreshRateConfigs = - std::make_unique<scheduler::RefreshRateConfigs>(displayState.physical->supportedModes, - displayState.physical->activeMode - ->getId(), - config); - const auto currRefreshRate = displayState.physical->activeMode->getFps(); + const auto currRefreshRate = display->getActiveMode()->getFps(); mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate, hal::PowerMode::OFF); @@ -3188,7 +3182,7 @@ void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) { mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs()); // start the EventThread - mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this); + mScheduler = getFactory().createScheduler(display->holdRefreshRateConfigs(), *this); const auto configs = mVsyncConfiguration->getCurrentConfigs(); const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs(); mAppConnectionHandle = @@ -3217,9 +3211,7 @@ void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) { // This is a bit hacky, but this avoids a back-pointer into the main SF // classes from EventThread, and there should be no run-time binder cost // anyway since there are no connected apps at this point. - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, displayId, - displayState.physical->activeMode->getId(), - vsyncPeriod); + mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode()); static auto ignorePresentFences = base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false); mScheduler->setIgnorePresentFences( @@ -3433,9 +3425,9 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind composerState.state.surface = handle; states.add(composerState); - lbc->updateTransformHint(mDefaultDisplayTransformHint); + lbc->updateTransformHint(mActiveDisplayTransformHint); if (outTransformHint) { - *outTransformHint = mDefaultDisplayTransformHint; + *outTransformHint = mActiveDisplayTransformHint; } // attach this layer to the client client->attachLayer(handle, lbc); @@ -3872,6 +3864,12 @@ uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) { flags |= eDisplayTransactionNeeded; } } + if (what & DisplayState::eFlagsChanged) { + if (state.flags != s.flags) { + state.flags = s.flags; + flags |= eDisplayTransactionNeeded; + } + } if (what & DisplayState::eDisplayProjectionChanged) { if (state.orientation != s.orientation) { state.orientation = s.orientation; @@ -4130,10 +4128,10 @@ uint32_t SurfaceFlinger::setClientStateLocked( } if (what & layer_state_t::eInputInfoChanged) { if (privileged) { - layer->setInputInfo(*s.inputHandle->getInfo()); + layer->setInputInfo(*s.windowInfoHandle->getInfo()); flags |= eTraversalNeeded; } else { - ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER"); + ALOGE("Attempt to update WindowInfo without permission ACCESS_SURFACE_FLINGER"); } } std::optional<nsecs_t> dequeueBufferTimestamp; @@ -4206,6 +4204,16 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } } + if (what & layer_state_t::eDropInputModeChanged) { + if (privileged) { + if (layer->setDropInputMode(s.dropInputMode)) { + flags |= eTraversalNeeded; + mInputInfoChanged = true; + } + } else { + ALOGE("Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER"); + } + } // This has to happen after we reparent children because when we reparent to null we remove // child layers from current state and remove its relative z. If the children are reparented in // the same transaction, then we have to make sure we reparent the children first so we do not @@ -4231,17 +4239,30 @@ uint32_t SurfaceFlinger::setClientStateLocked( } bool bufferChanged = what & layer_state_t::eBufferChanged; bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged; + bool bufferSizeExceedsLimit = false; std::shared_ptr<renderengine::ExternalTexture> buffer; if (bufferChanged && cacheIdChanged && s.buffer != nullptr) { - ClientCache::getInstance().add(s.cachedBuffer, s.buffer); - buffer = ClientCache::getInstance().get(s.cachedBuffer); + bufferSizeExceedsLimit = + exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight()); + if (!bufferSizeExceedsLimit) { + ClientCache::getInstance().add(s.cachedBuffer, s.buffer); + buffer = ClientCache::getInstance().get(s.cachedBuffer); + } } else if (cacheIdChanged) { buffer = ClientCache::getInstance().get(s.cachedBuffer); } else if (bufferChanged && s.buffer != nullptr) { - buffer = std::make_shared< - renderengine::ExternalTexture>(s.buffer, getRenderEngine(), - renderengine::ExternalTexture::Usage::READABLE); - } + bufferSizeExceedsLimit = + exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight()); + if (!bufferSizeExceedsLimit) { + buffer = std::make_shared< + renderengine::ExternalTexture>(s.buffer, getRenderEngine(), + renderengine::ExternalTexture::Usage::READABLE); + } + } + ALOGE_IF(bufferSizeExceedsLimit, + "Attempted to create an ExternalTexture for layer %s that exceeds render target size " + "limit.", + layer->getDebugName()); if (buffer) { const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged; const uint64_t frameNumber = frameNumberChanged @@ -4250,7 +4271,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp, s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo, - s.releaseBufferListener)) { + s.releaseBufferListener, s.releaseBufferEndpoint)) { flags |= eTraversalNeeded; } } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { @@ -4524,9 +4545,10 @@ void SurfaceFlinger::onInitializeDisplays() { {}, getpid(), getuid(), 0 /* Undefined transactionId */); setPowerModeInternal(display, hal::PowerMode::ON); - const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); + const nsecs_t vsyncPeriod = + display->refreshRateConfigs().getCurrentRefreshRate().getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod); - mDefaultDisplayTransformHint = display->getTransformHint(); + mActiveDisplayTransformHint = display->getTransformHint(); // Use phase of 0 since phase is not known. // Use latency of 0, which will snap to the ideal latency. DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod}; @@ -4538,6 +4560,26 @@ void SurfaceFlinger::initializeDisplays() { static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); })); } +sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const { + sp<DisplayDevice> display; + for (const auto& pair : mDisplays) { + const auto& displayDevice = pair.second; + if (!displayDevice->receivesInput() || + !displayDevice->getCompositionDisplay() + ->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) { + continue; + } + // Don't return immediately so that we can log duplicates. + if (display) { + ALOGE("Multiple display devices claim to accept input for the same layerstack: %d", + layer->getLayerStack()); + continue; + } + display = displayDevice; + } + return display; +} + void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { if (display->isVirtual()) { ALOGE("%s: Invalid operation on virtual display", __FUNCTION__); @@ -4552,13 +4594,23 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: return; } + const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken); + if (activeDisplay != display && display->isInternal() && activeDisplay && + activeDisplay->isPoweredOn()) { + ALOGW("Trying to change power mode on non active display while the active display is ON"); + } + display->setPowerMode(mode); if (mInterceptor->isEnabled()) { mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode)); } - const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); + const auto vsyncPeriod = display->refreshRateConfigs().getCurrentRefreshRate().getVsyncPeriod(); if (currentMode == hal::PowerMode::OFF) { + // Turn on the display + if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) { + onActiveDisplayChangedLocked(display); + } // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315. // We can merge the syscall later. if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) { @@ -4568,8 +4620,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno)); } getHwComposer().setPowerMode(displayId, mode); - if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) { - getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState); + if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) { + setHWCVsyncEnabled(displayId, mHWCVsyncPendingState); mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, vsyncPeriod); } @@ -4585,13 +4637,13 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) { ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno)); } - if (display->isPrimary() && currentMode != hal::PowerMode::DOZE_SUSPEND) { + if (isDisplayActiveLocked(display) && currentMode != hal::PowerMode::DOZE_SUSPEND) { mScheduler->disableHardwareVsync(true); mScheduler->onScreenReleased(mAppConnectionHandle); } // Make sure HWVsync is disabled before turning off the display - getHwComposer().setVsyncEnabled(displayId, hal::Vsync::DISABLE); + setHWCVsyncEnabled(displayId, hal::Vsync::DISABLE); getHwComposer().setPowerMode(displayId, mode); mVisibleRegionsDirty = true; @@ -4599,13 +4651,13 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) { // Update display while dozing getHwComposer().setPowerMode(displayId, mode); - if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) { + if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) { mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, vsyncPeriod); } } else if (mode == hal::PowerMode::DOZE_SUSPEND) { // Leave display going to doze - if (display->isPrimary()) { + if (isDisplayActiveLocked(display)) { mScheduler->disableHardwareVsync(true); mScheduler->onScreenReleased(mAppConnectionHandle); } @@ -4615,7 +4667,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: getHwComposer().setPowerMode(displayId, mode); } - if (display->isPrimary()) { + if (isDisplayActiveLocked(display)) { mTimeStats->setPowerMode(mode); mRefreshRateStats->setPowerMode(mode); mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON); @@ -4685,12 +4737,17 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { } if (dumpLayers) { - const LayersProto layersProto = dumpProtoFromMainThread(); + LayersTraceFileProto traceFileProto = SurfaceTracing::createLayersTraceFileProto(); + LayersTraceProto* layersTrace = traceFileProto.add_entry(); + LayersProto layersProto = dumpProtoFromMainThread(); + layersTrace->mutable_layers()->Swap(&layersProto); + dumpDisplayProto(*layersTrace); + if (asProto) { - result.append(layersProto.SerializeAsString()); + result.append(traceFileProto.SerializeAsString()); } else { // Dump info that we need to access from the main thread - const auto layerTree = LayerProtoParser::generateLayerTree(layersProto); + const auto layerTree = LayerProtoParser::generateLayerTree(layersTrace->layers()); result.append(LayerProtoParser::layerTreeToString(layerTree)); result.append("\n"); dumpOffscreenLayers(result); @@ -4784,13 +4841,13 @@ void SurfaceFlinger::dumpVSync(std::string& result) const { " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", dispSyncPresentTimeOffset, getVsyncPeriodFromHWC()); - mRefreshRateConfigs->dump(result); - StringAppendF(&result, "(mode override by backdoor: %s)\n\n", mDebugDisplayModeSetByBackdoor ? "yes" : "no"); mScheduler->dump(mAppConnectionHandle, result); mScheduler->dumpVsync(result); + StringAppendF(&result, "mHWCVsyncPendingState=%s mLastHWCVsyncState=%s\n", + to_string(mHWCVsyncPendingState).c_str(), to_string(mLastHWCVsyncState).c_str()); } void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const { @@ -4961,6 +5018,22 @@ LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { return layersProto; } +void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const { + for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) { + DisplayProto* displayProto = layersTraceProto.add_displays(); + displayProto->set_id(display->getId().value); + displayProto->set_name(display->getDisplayName()); + displayProto->set_layer_stack(display->getLayerStack()); + LayerProtoHelper::writeSizeToProto(display->getWidth(), display->getHeight(), + [&]() { return displayProto->mutable_size(); }); + LayerProtoHelper::writeToProto(display->getLayerStackSpaceRect(), [&]() { + return displayProto->mutable_layer_stack_space_rect(); + }); + LayerProtoHelper::writeTransformToProto(display->getTransform(), + displayProto->mutable_transform()); + } +} + void SurfaceFlinger::dumpHwc(std::string& result) const { getHwComposer().dump(result); } @@ -5149,7 +5222,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co continue; } - StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str()); + StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(), + (isDisplayActiveLocked(display) ? "active" : "inactive")); Layer::miniDumpHeader(result); const DisplayDevice& ref = *display; @@ -5349,6 +5423,14 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { } return PERMISSION_DENIED; } + case ADD_WINDOW_INFOS_LISTENER: + case REMOVE_WINDOW_INFOS_LISTENER: { + const int uid = IPCThreadState::self()->getCallingUid(); + if (uid == AID_SYSTEM || uid == AID_GRAPHICS) { + return OK; + } + return PERMISSION_DENIED; + } } // These codes are used for the IBinder protocol to either interrogate the recipient @@ -5662,16 +5744,17 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return NO_ERROR; } case 1034: { - switch (n = data.readInt32()) { - case 0: - case 1: - enableRefreshRateOverlay(static_cast<bool>(n)); - break; - default: { - Mutex::Autolock lock(mStateLock); - reply->writeBool(mRefreshRateOverlay != nullptr); + schedule([&] { + switch (n = data.readInt32()) { + case 0: + case 1: + ON_MAIN_THREAD(enableRefreshRateOverlay(static_cast<bool>(n))); + break; + default: { + reply->writeBool(ON_MAIN_THREAD(isRefreshRateOverlayEnabled())); + } } - } + }).get(); return NO_ERROR; } case 1035: { @@ -5691,7 +5774,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return std::make_optional<PhysicalDisplayId>(inputDisplayId); } - return getInternalDisplayId(); + return getDefaultDisplayDevice()->getPhysicalId(); }(); if (!displayId) { @@ -5817,22 +5900,25 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { static bool updateOverlay = property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true); if (!updateOverlay) return; - if (Mutex::Autolock lock(mStateLock); !mRefreshRateOverlay) return; + if (Mutex::Autolock lock(mStateLock); !isRefreshRateOverlayEnabled()) return; // Update the overlay on the main thread to avoid race conditions with // mRefreshRateConfigs->getCurrentRefreshRate() static_cast<void>(schedule([=] { - const auto desiredActiveMode = getDesiredActiveMode(); - const std::optional<DisplayModeId> desiredModeId = - desiredActiveMode ? std::make_optional(desiredActiveMode->modeId) : std::nullopt; + const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); + if (!display) { + ALOGW("%s: default display is null", __func__); + return; + } + + const auto desiredActiveMode = display->getDesiredActiveMode(); + const std::optional<DisplayModeId> desiredModeId = desiredActiveMode + ? std::make_optional(desiredActiveMode->mode->getId()) + : std::nullopt; const bool timerExpired = mKernelIdleTimerEnabled && expired; - const auto newRefreshRate = - mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired); - if (newRefreshRate) { - if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) { - mRefreshRateOverlay->changeRefreshRate(*newRefreshRate); - } + + if (display->onKernelTimerChanged(desiredModeId, timerExpired)) { mEventQueue->invalidate(); } })); @@ -5841,12 +5927,19 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { void SurfaceFlinger::toggleKernelIdleTimer() { using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction; - // If the support for kernel idle timer is disabled in SF code, don't do anything. - if (!mSupportKernelIdleTimer) { + const auto display = getDefaultDisplayDeviceLocked(); + if (!display) { + ALOGW("%s: default display is null", __func__); return; } - const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction(); + // If the support for kernel idle timer is disabled for the active display, + // don't do anything. + if (!display->refreshRateConfigs().supportsKernelIdleTimer()) { + return; + } + + const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction(); switch (action) { case KernelIdleTimerAction::TurnOff: if (mKernelIdleTimerEnabled) { @@ -6083,9 +6176,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, sp<Layer> parent; Rect crop(args.sourceCrop); std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers; - Rect layerStackSpaceRect; ui::Dataspace dataspace; - bool captureSecureLayers; // Call this before holding mStateLock to avoid any deadlocking. bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission(); @@ -6094,7 +6185,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, Mutex::Autolock lock(mStateLock); parent = fromHandle(args.layerHandle).promote(); - if (parent == nullptr || parent->isRemovedFromCurrentState()) { + if (parent == nullptr) { ALOGE("captureLayers called with an invalid or removed parent"); return NAME_NOT_FOUND; } @@ -6133,39 +6224,36 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } } - const auto display = findDisplay(WithLayerStack(parent->getLayerStack())); - if (!display) { - return NAME_NOT_FOUND; - } - - layerStackSpaceRect = display->getLayerStackSpaceRect(); - // The dataspace is depended on the color mode of display, that could use non-native mode // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes, // 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(WithLayerStack(parent->getLayerStack())); + 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); } - captureSecureLayers = args.captureSecureLayers && display->isSecure(); } // mStateLock // really small crop or frameScale - if (reqSize.width <= 0) { - reqSize.width = 1; - } - if (reqSize.height <= 0) { - reqSize.height = 1; + if (reqSize.width <= 0 || reqSize.height <= 0) { + ALOGW("Failed to captureLayes: crop or scale too small"); + return BAD_VALUE; } + Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height); bool childrenOnly = args.childrenOnly; RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> { return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace, childrenOnly, layerStackSpaceRect, - captureSecureLayers); + args.captureSecureLayers); }); auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) { @@ -6202,6 +6290,13 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, const sp<IScreenCaptureListener>& captureListener) { ATRACE_CALL(); + if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) { + ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32 + ") that exceeds render target size limit.", + bufferSize.getWidth(), bufferSize.getHeight()); + return BAD_VALUE; + } + // Loop over all visible layers to see whether there's any protected layer. A protected layer is // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer. // A protected layer has no implication on whether it's secure, which is explicitly set by @@ -6416,7 +6511,7 @@ status_t SurfaceFlinger::renderScreenImplLocked( return NO_ERROR; } -void SurfaceFlinger::setInputWindowsFinished() { +void SurfaceFlinger::windowInfosReported() { Mutex::Autolock _l(mStateLock); signalSynchronousTransactions(CountDownLatch::eSyncInputWindows); } @@ -6464,78 +6559,55 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) { Mutex::Autolock lock(mStateLock); - LOG_ALWAYS_FATAL_IF(!display->isPrimary() && overridePolicy, - "Can only set override policy on the primary display"); - LOG_ALWAYS_FATAL_IF(!policy && !overridePolicy, "Can only clear the override policy"); - if (mDebugDisplayModeSetByBackdoor) { // ignore this request as mode is overridden by backdoor return NO_ERROR; } - if (!display->isPrimary()) { - // TODO(b/144711714): For non-primary displays we should be able to set an active mode - // as well. For now, just call directly to initiateModeChange but ideally - // it should go thru setDesiredActiveMode, similar to primary display. - ALOGV("%s for non-primary display", __func__); - const auto displayId = display->getPhysicalId(); - - hal::VsyncPeriodChangeConstraints constraints; - constraints.desiredTimeNanos = systemTime(); - constraints.seamlessRequired = false; - - hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0}; - if (display->initiateModeChange(policy->defaultMode, constraints, &timeline) != NO_ERROR) { - return BAD_VALUE; - } - if (timeline.refreshRequired) { - repaintEverythingForHWC(); - } - - display->setActiveMode(policy->defaultMode); - const nsecs_t vsyncPeriod = display->getMode(policy->defaultMode)->getVsyncPeriod(); - mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, displayId, - policy->defaultMode, vsyncPeriod); - return NO_ERROR; - } - status_t setPolicyResult = overridePolicy - ? mRefreshRateConfigs->setOverridePolicy(policy) - : mRefreshRateConfigs->setDisplayManagerPolicy(*policy); + ? display->refreshRateConfigs().setOverridePolicy(policy) + : display->refreshRateConfigs().setDisplayManagerPolicy(*policy); if (setPolicyResult < 0) { return BAD_VALUE; } if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) { return NO_ERROR; } - scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy(); + + scheduler::RefreshRateConfigs::Policy currentPolicy = + display->refreshRateConfigs().getCurrentPolicy(); ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might // be depending in this callback. const auto activeMode = display->getActiveMode(); - const nsecs_t vsyncPeriod = activeMode->getVsyncPeriod(); - const auto physicalId = display->getPhysicalId(); - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId, activeMode->getId(), - vsyncPeriod); - toggleKernelIdleTimer(); + if (isDisplayActiveLocked(display)) { + mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); + toggleKernelIdleTimer(); + } else { + mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); + } + + const DisplayModePtr preferredDisplayMode = [&] { + const auto schedulerMode = mScheduler->getPreferredDisplayMode(); + if (schedulerMode && schedulerMode->getPhysicalDisplayId() == display->getPhysicalId()) { + return schedulerMode; + } + + return display->getMode(currentPolicy.defaultMode); + }(); - auto modeId = mScheduler->getPreferredModeId(); - auto preferredRefreshRate = modeId - ? mRefreshRateConfigs->getRefreshRateFromModeId(*modeId) - // NOTE: Choose the default mode ID, if Scheduler doesn't have one in mind. - : mRefreshRateConfigs->getRefreshRateFromModeId(currentPolicy.defaultMode); ALOGV("trying to switch to Scheduler preferred mode %d (%s)", - preferredRefreshRate.getModeId().value(), preferredRefreshRate.getName().c_str()); + preferredDisplayMode->getId().value(), to_string(preferredDisplayMode->getFps()).c_str()); - if (isDisplayModeAllowed(preferredRefreshRate.getModeId())) { + if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) { ALOGV("switching to Scheduler preferred display mode %d", - preferredRefreshRate.getModeId().value()); - setDesiredActiveMode({preferredRefreshRate.getModeId(), Scheduler::ModeEvent::Changed}); + preferredDisplayMode->getId().value()); + setDesiredActiveMode({preferredDisplayMode, Scheduler::ModeEvent::Changed}); } else { LOG_ALWAYS_FATAL("Desired display mode not allowed: %d", - preferredRefreshRate.getModeId().value()); + preferredDisplayMode->getId().value()); } return NO_ERROR; @@ -6595,29 +6667,19 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return NAME_NOT_FOUND; } - if (display->isPrimary()) { - scheduler::RefreshRateConfigs::Policy policy = - mRefreshRateConfigs->getDisplayManagerPolicy(); - *outDefaultMode = policy.defaultMode.value(); - *outAllowGroupSwitching = policy.allowGroupSwitching; - *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue(); - *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue(); - *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue(); - *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue(); - return NO_ERROR; - } else if (display->isVirtual()) { + if (display->isVirtual()) { return INVALID_OPERATION; - } else { - const auto activeMode = display->getActiveMode(); - *outDefaultMode = activeMode->getId().value(); - *outAllowGroupSwitching = false; - auto vsyncPeriod = activeMode->getVsyncPeriod(); - *outPrimaryRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue(); - *outPrimaryRefreshRateMax = Fps::fromPeriodNsecs(vsyncPeriod).getValue(); - *outAppRequestRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue(); - *outAppRequestRefreshRateMax = Fps::fromPeriodNsecs(vsyncPeriod).getValue(); - return NO_ERROR; } + + scheduler::RefreshRateConfigs::Policy policy = + display->refreshRateConfigs().getDisplayManagerPolicy(); + *outDefaultMode = policy.defaultMode.value(); + *outAllowGroupSwitching = policy.allowGroupSwitching; + *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue(); + *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue(); + *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue(); + *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue(); + return NO_ERROR; } wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const { @@ -6736,7 +6798,8 @@ status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) // matter for the override policy though, since we set allowGroupSwitching to // true, so it's not a problem. scheduler::RefreshRateConfigs::Policy overridePolicy; - overridePolicy.defaultMode = mRefreshRateConfigs->getDisplayManagerPolicy().defaultMode; + overridePolicy.defaultMode = + display->refreshRateConfigs().getDisplayManagerPolicy().defaultMode; overridePolicy.allowGroupSwitching = true; constexpr bool kOverridePolicy = true; result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy); @@ -6804,25 +6867,11 @@ status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& } void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { - static_cast<void>(schedule([=] { - std::unique_ptr<RefreshRateOverlay> overlay; - if (enable) { - overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner); - } - - { - Mutex::Autolock lock(mStateLock); - - // Destroy the layer of the current overlay, if any, outside the lock. - mRefreshRateOverlay.swap(overlay); - if (!mRefreshRateOverlay) return; - - if (const auto display = getDefaultDisplayDeviceLocked()) { - mRefreshRateOverlay->setViewport(display->getSize()); - mRefreshRateOverlay->changeRefreshRate(display->getActiveMode()->getFps()); - } + for (const auto& [ignored, display] : mDisplays) { + if (display->isInternal()) { + display->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner); } - })); + } } status_t SurfaceFlinger::addTransactionTraceListener( @@ -6850,7 +6899,14 @@ int SurfaceFlinger::calculateMaxAcquiredBufferCount(Fps refreshRate, } status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const { - const auto maxSupportedRefreshRate = mRefreshRateConfigs->getSupportedRefreshRateRange().max; + const auto maxSupportedRefreshRate = [&] { + const auto display = getDefaultDisplayDevice(); + if (display) { + return display->refreshRateConfigs().getSupportedRefreshRateRange().max; + } + ALOGW("%s: default display is null", __func__); + return Fps(60); + }(); *buffers = getMaxAcquiredBufferCountForRefreshRate(maxSupportedRefreshRate); return NO_ERROR; } @@ -6861,7 +6917,14 @@ int SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) co if (frameRateOverride.has_value()) { return frameRateOverride.value(); } - return mRefreshRateConfigs->getCurrentRefreshRate().getFps(); + + const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); + if (display) { + return display->refreshRateConfigs().getCurrentRefreshRate().getFps(); + } + + ALOGW("%s: default display is null", __func__); + return Fps(60); }(); return getMaxAcquiredBufferCountForRefreshRate(refreshRate); } @@ -6948,7 +7011,7 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) { parent->addChild(layer); } - layer->updateTransformHint(mDefaultDisplayTransformHint); + layer->updateTransformHint(mActiveDisplayTransformHint); if (state->initialProducer != nullptr) { mGraphicBufferProducerList.insert(state->initialProducer); @@ -6978,6 +7041,47 @@ void SurfaceFlinger::notifyRegionSamplingThread() { mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate()); } +void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) { + mScheduler->onActiveDisplayAreaChanged(activeDisplay->getWidth() * activeDisplay->getHeight()); + getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize()); +} + +void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) { + ATRACE_CALL(); + + if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) { + display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false); + } + + if (!activeDisplay) { + ALOGE("%s: activeDisplay is null", __func__); + return; + } + mActiveDisplayToken = activeDisplay->getDisplayToken(); + activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); + updateInternalDisplayVsyncLocked(activeDisplay); + mScheduler->setModeChangePending(false); + mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs()); + onActiveDisplaySizeChanged(activeDisplay); + mActiveDisplayTransformHint = activeDisplay->getTransformHint(); + + // Update the kernel timer for the current active display, since the policy + // for this display might have changed when it was not the active display. + toggleKernelIdleTimer(); +} + +status_t SurfaceFlinger::addWindowInfosListener( + const sp<IWindowInfosListener>& windowInfosListener) const { + mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeWindowInfosListener( + const sp<IWindowInfosListener>& windowInfosListener) const { + mWindowInfosListenerInvoker->removeWindowInfosListener(windowInfosListener); + return NO_ERROR; +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 4e8e614cbd..74fe7d97d0 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -93,7 +93,6 @@ class FpsReporter; class TunnelModeEnabledReporter; class HdrLayerInfoReporter; class HWComposer; -struct SetInputWindowsListener; class IGraphicBufferProducer; class Layer; class MessageBase; @@ -102,6 +101,7 @@ class RegionSamplingThread; class RenderArea; class TimeStats; class FrameTracer; +class WindowInfosListenerInvoker; using gui::ScreenCaptureResults; @@ -332,11 +332,12 @@ public: // If set, disables reusing client composition buffers. This can be set by // debug.sf.disable_client_composition_cache bool mDisableClientCompositionCache = false; - void setInputWindowsFinished(); + void windowInfosReported(); // Disables expensive rendering for all displays // This is scheduled on the main thread void disableExpensiveRendering(); + FloatRect getMaxDisplayBounds(); protected: // We're reference counted, never destroy SurfaceFlinger directly @@ -350,12 +351,14 @@ protected: REQUIRES(mStateLock); virtual void commitTransactionLocked(); - // Used internally by computeLayerBounds() to gets the clip rectangle to use for the - // root layers on a particular display in layer-coordinate space. The - // layers (and effectively their children) will be clipped against this - // rectangle. The base behavior is to clip to the visible region of the - // display. - virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const; + virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&) + REQUIRES(mStateLock); + + // Returns true if any display matches a `bool(const DisplayDevice&)` predicate. + template <typename Predicate> + bool hasDisplay(Predicate p) const REQUIRES(mStateLock) { + return static_cast<bool>(findDisplay(p)); + } private: friend class BufferLayer; @@ -455,14 +458,7 @@ private: mCounterByLayerHandle GUARDED_BY(mLock); }; - struct ActiveModeInfo { - DisplayModeId modeId; - Scheduler::ModeEvent event = Scheduler::ModeEvent::None; - - bool operator!=(const ActiveModeInfo& other) const { - return modeId != other.modeId || event != other.event; - } - }; + using ActiveModeInfo = DisplayDevice::ActiveModeInfo; enum class BootStage { BOOTLOADER, @@ -593,7 +589,7 @@ private: typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)> void modulateVsync(Handler handler, Args... args) { if (const auto config = (*mVsyncModulator.*handler)(args...)) { - const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); + const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateConfigs(); setVsyncConfig(*config, vsyncPeriod); } } @@ -612,8 +608,12 @@ private: sp<ISurfaceComposerClient> createConnection() override; sp<IBinder> createDisplay(const String8& displayName, bool secure) override; void destroyDisplay(const sp<IBinder>& displayToken) override; - std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override; + std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override EXCLUDES(mStateLock) { + Mutex::Autolock lock(mStateLock); + return getPhysicalDisplayIdsLocked(); + } status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override EXCLUDES(mStateLock); + sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override; status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& state, @@ -721,6 +721,11 @@ private: status_t getMaxAcquiredBufferCount(int* buffers) const override; + status_t addWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) const override; + status_t removeWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) const override; + // Implements IBinder::DeathRecipient. void binderDied(const wp<IBinder>& who) override; @@ -751,12 +756,10 @@ private: // Called when the frame rate override list changed to trigger an event. void triggerOnFrameRateOverridesChanged() override; // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. - void toggleKernelIdleTimer(); + void toggleKernelIdleTimer() 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; - // Keeps track of whether the kernel timer is supported on the SF side. - bool mSupportKernelIdleTimer = false; // Show spinner with refresh rate overlay bool mRefreshRateOverlaySpinner = false; @@ -780,9 +783,9 @@ private: // Calls to setActiveMode on the main thread if there is a pending mode change // that needs to be applied. void performSetActiveMode() REQUIRES(mStateLock); - void clearDesiredActiveModeState() REQUIRES(mStateLock) EXCLUDES(mActiveModeLock); + void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock); // Called when active mode is no longer is progress - void desiredActiveModeChangeDone() REQUIRES(mStateLock); + void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock); // Called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) REQUIRES(mStateLock); @@ -811,11 +814,11 @@ private: void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock); void updateInputFlinger(); - void updateInputWindowInfo(); + void notifyWindowInfos(); void commitInputWindowCommands() REQUIRES(mStateLock); void updateCursorAsync(); - void initScheduler(const DisplayDeviceState&) REQUIRES(mStateLock); + void initScheduler(const sp<DisplayDevice>& display) REQUIRES(mStateLock); void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock); void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod); @@ -931,8 +934,9 @@ private: void readPersistentProperties(); - size_t getMaxTextureSize() const; - size_t getMaxViewportDims() const; + bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const { + return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize; + } int getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const; @@ -954,6 +958,10 @@ private: sp<const DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) const REQUIRES(mStateLock) { + return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(id); + } + + sp<DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) REQUIRES(mStateLock) { if (const auto token = getPhysicalDisplayTokenLocked(id)) { return getDisplayDeviceLocked(token); } @@ -965,13 +973,18 @@ private: } sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) { + if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) { + return display; + } + // The active display is outdated, fall back to the internal display + mActiveDisplayToken.clear(); if (const auto token = getInternalDisplayTokenLocked()) { return getDisplayDeviceLocked(token); } return nullptr; } - sp<const DisplayDevice> getDefaultDisplayDevice() EXCLUDES(mStateLock) { + sp<const DisplayDevice> getDefaultDisplayDevice() const EXCLUDES(mStateLock) { Mutex::Autolock lock(mStateLock); return getDefaultDisplayDeviceLocked(); } @@ -990,10 +1003,18 @@ private: return findDisplay([id](const auto& display) { return display.getId() == id; }); } + std::vector<PhysicalDisplayId> getPhysicalDisplayIdsLocked() const REQUIRES(mStateLock); + // mark a region of a layer stack dirty. this updates the dirty // region of all screens presenting this layer stack. void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty); + sp<DisplayDevice> getDisplayWithInputByLayer(Layer* layer) const REQUIRES(mStateLock); + + bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) { + return display->getDisplayToken() == mActiveDisplayToken; + } + /* * H/W composer */ @@ -1031,8 +1052,6 @@ private: const sp<compositionengine::DisplaySurface>& displaySurface, const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock); void processDisplayChangesLocked() REQUIRES(mStateLock); - void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&) - REQUIRES(mStateLock); void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock); void processDisplayChanged(const wp<IBinder>& displayToken, const DisplayDeviceState& currentState, @@ -1046,12 +1065,15 @@ private: */ nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock); + void setHWCVsyncEnabled(PhysicalDisplayId id, hal::Vsync enabled) { + mLastHWCVsyncState = enabled; + getHwComposer().setVsyncEnabled(id, enabled); + } + // Sets the refresh rate by switching active configs, if they are available for // the desired refresh rate. void changeRefreshRateLocked(const RefreshRate&, Scheduler::ModeEvent) REQUIRES(mStateLock); - bool isDisplayModeAllowed(DisplayModeId) const REQUIRES(mStateLock); - struct FenceWithFenceTime { sp<Fence> fence = Fence::NO_FENCE; std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE; @@ -1112,10 +1134,13 @@ private: void enableHalVirtualDisplays(bool); // Virtual display lifecycle for ID generation and HAL allocation. - VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, ui::LayerStack) - REQUIRES(mStateLock); + VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock); void releaseVirtualDisplay(VirtualDisplayId); + void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock); + + void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay); + /* * Debugging & dumpsys */ @@ -1143,6 +1168,8 @@ private: LayersProto dumpDrawingStateProto(uint32_t traceFlags) const; void dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; + void dumpDisplayProto(LayersTraceProto& layersTraceProto) const; + // Dumps state from HW Composer void dumpHwc(std::string& result) const; LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) @@ -1181,13 +1208,6 @@ private: /* * Misc */ - - std::optional<ActiveModeInfo> getDesiredActiveMode() EXCLUDES(mActiveModeLock) { - std::lock_guard<std::mutex> lock(mActiveModeLock); - if (mDesiredActiveModeChanged) return mDesiredActiveMode; - return std::nullopt; - } - std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId) REQUIRES(mStateLock); @@ -1195,6 +1215,9 @@ private: std::chrono::nanoseconds presentLatency); int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const; + void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) + REQUIRES(mStateLock); + sp<StartPropertySetThread> mStartPropertySetThread; surfaceflinger::Factory& mFactory; @@ -1379,6 +1402,9 @@ private: SurfaceFlingerBE mBE; std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine; + // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by + // any mutex. + size_t mMaxRenderTargetSize{1}; const std::string mHwcServiceName; @@ -1397,24 +1423,14 @@ private: // Optional to defer construction until PhaseConfiguration is created. sp<VsyncModulator> mVsyncModulator; - std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; std::atomic<nsecs_t> mExpectedPresentTime = 0; nsecs_t mScheduledPresentTime = 0; hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE; - - std::mutex mActiveModeLock; - // This bit is set once we start setting the mode. We read from this bit during the - // process. If at the end, this bit is different than mDesiredActiveMode, we restart - // the process. - ActiveModeInfo mUpcomingActiveMode; // Always read and written on the main thread. - // This bit can be set at any point in time when the system wants the new mode. - ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock); + hal::Vsync mLastHWCVsyncState = hal::Vsync::DISABLE; // below flags are set by main thread only - TracedOrdinal<bool> mDesiredActiveModeChanged - GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false}; bool mSetActiveModePending = false; bool mLumaSampling = true; @@ -1430,15 +1446,12 @@ private: // Should only be accessed by the main thread. InputWindowCommands mInputWindowCommands; - sp<SetInputWindowsListener> mSetInputWindowsListener; - Hwc2::impl::PowerAdvisor mPowerAdvisor; // This should only be accessed on the main thread. nsecs_t mFrameStartTime = 0; - void enableRefreshRateOverlay(bool enable); - std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay GUARDED_BY(mStateLock); + void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock); // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; @@ -1489,10 +1502,21 @@ private: auto getLayerCreatedState(const sp<IBinder>& handle); sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock); - std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint; + std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint; void scheduleRegionSamplingThread(); void notifyRegionSamplingThread(); + + bool isRefreshRateOverlayEnabled() const REQUIRES(mStateLock) { + return std::any_of(mDisplays.begin(), mDisplays.end(), + [](std::pair<wp<IBinder>, sp<DisplayDevice>> display) { + return display.second->isRefreshRateOverlayEnabled(); + }); + } + + wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock); + + const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp index 4a7518066f..89d1c4d327 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp @@ -65,8 +65,9 @@ std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfig } std::unique_ptr<Scheduler> DefaultFactory::createScheduler( - const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) { - return std::make_unique<Scheduler>(configs, callback); + const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs, + ISchedulerCallback& callback) { + return std::make_unique<Scheduler>(std::move(refreshRateConfigs), callback); } sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() { diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h index 24148ddf9d..b8bf2baa33 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h @@ -30,8 +30,8 @@ public: std::unique_ptr<MessageQueue> createMessageQueue() override; std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) override; - std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&, - ISchedulerCallback&) override; + std::unique_ptr<Scheduler> createScheduler( + const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override; sp<SurfaceInterceptor> createSurfaceInterceptor() override; sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override; sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override; diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index 885297fc30..13c95ddd15 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -79,8 +79,8 @@ public: virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0; virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) = 0; - virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&, - ISchedulerCallback&) = 0; + virtual std::unique_ptr<Scheduler> createScheduler( + const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) = 0; virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0; virtual sp<StartPropertySetThread> createStartPropertySetThread( diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 837b2e8924..9be3abefab 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -161,6 +161,7 @@ void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface); addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack); + addDisplayFlagsLocked(transaction, display.sequenceId, display.flags); addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height); addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation), display.layerStackSpaceRect, display.orientedDisplaySpaceRect); @@ -481,6 +482,9 @@ void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction, if (state.what & DisplayState::eLayerStackChanged) { addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack); } + if (state.what & DisplayState::eFlagsChanged) { + addDisplayFlagsLocked(transaction, sequenceId, state.flags); + } if (state.what & DisplayState::eDisplaySizeChanged) { addDisplaySizeLocked(transaction, sequenceId, state.width, state.height); } @@ -572,6 +576,13 @@ void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, layerStackChange->set_layer_stack(layerStack); } +void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, + uint32_t flags) { + DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); + DisplayFlagsChange* flagsChange(dispChange->mutable_flags()); + flagsChange->set_flags(flags); +} + void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w, uint32_t h) { diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index c9555969dc..7b331b9f5f 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -48,7 +48,7 @@ using SurfaceChange = surfaceflinger::SurfaceChange; using Increment = surfaceflinger::Increment; using DisplayChange = surfaceflinger::DisplayChange; -constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb"; +constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope"; class SurfaceInterceptor : public IBinder::DeathRecipient { public: @@ -185,6 +185,7 @@ private: const sp<const IGraphicBufferProducer>& surface); void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, uint32_t layerStack); + void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags); void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w, uint32_t h); void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId, diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp index b4d8a9adce..596373732f 100644 --- a/services/surfaceflinger/SurfaceTracing.cpp +++ b/services/surfaceflinger/SurfaceTracing.cpp @@ -137,14 +137,19 @@ status_t SurfaceTracing::Runner::stop() { return writeToFile(); } +LayersTraceFileProto SurfaceTracing::createLayersTraceFileProto() { + LayersTraceFileProto fileProto; + fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | + LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); + return fileProto; +} + status_t SurfaceTracing::Runner::writeToFile() { ATRACE_CALL(); - LayersTraceFileProto fileProto; + LayersTraceFileProto fileProto = createLayersTraceFileProto(); std::string output; - fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | - LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); mBuffer.flush(&fileProto); mBuffer.reset(mConfig.bufferSize); @@ -186,7 +191,7 @@ LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) { entry.set_excludes_composition_state(true); } entry.set_missed_entries(mMissedTraceEntries); - + mFlinger.dumpDisplayProto(entry); return entry; } diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h index 15a503d834..cea1a3324e 100644 --- a/services/surfaceflinger/SurfaceTracing.h +++ b/services/surfaceflinger/SurfaceTracing.h @@ -73,11 +73,12 @@ public: }; void setTraceFlags(uint32_t flags) { mConfig.flags = flags; } bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; } + static LayersTraceFileProto createLayersTraceFileProto(); private: class Runner; static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB; - static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb"; + static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope"; SurfaceFlinger& mFlinger; mutable std::mutex mTraceLock; diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp new file mode 100644 index 0000000000..dc2aa58c9a --- /dev/null +++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WindowInfosListenerInvoker.h" +#include <gui/ISurfaceComposer.h> +#include <unordered_set> +#include "SurfaceFlinger.h" + +namespace android { + +using gui::IWindowInfosListener; +using gui::WindowInfo; + +struct WindowInfosReportedListener : gui::BnWindowInfosReportedListener { + explicit WindowInfosReportedListener(std::function<void()> listenerCb) + : mListenerCb(listenerCb) {} + + binder::Status onWindowInfosReported() override { + if (mListenerCb != nullptr) { + mListenerCb(); + } + return binder::Status::ok(); + } + + std::function<void()> mListenerCb; +}; + +WindowInfosListenerInvoker::WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf) : mSf(sf) { + mWindowInfosReportedListener = + new WindowInfosReportedListener([&]() { windowInfosReported(); }); +} + +void WindowInfosListenerInvoker::addWindowInfosListener( + const sp<IWindowInfosListener>& windowInfosListener) { + sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener); + + asBinder->linkToDeath(this); + std::scoped_lock lock(mListenersMutex); + mWindowInfosListeners.emplace(asBinder, windowInfosListener); +} + +void WindowInfosListenerInvoker::removeWindowInfosListener( + const sp<IWindowInfosListener>& windowInfosListener) { + sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener); + + std::scoped_lock lock(mListenersMutex); + asBinder->unlinkToDeath(this); + mWindowInfosListeners.erase(asBinder); +} + +void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) { + std::scoped_lock lock(mListenersMutex); + mWindowInfosListeners.erase(who); +} + +void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos, + bool shouldSync) { + std::unordered_set<sp<IWindowInfosListener>, ISurfaceComposer::SpHash<IWindowInfosListener>> + windowInfosListeners; + + { + std::scoped_lock lock(mListenersMutex); + for (const auto& [_, listener] : mWindowInfosListeners) { + windowInfosListeners.insert(listener); + } + } + + mCallbacksPending = windowInfosListeners.size(); + + for (const auto& listener : windowInfosListeners) { + listener->onWindowInfosChanged(windowInfos, + shouldSync ? mWindowInfosReportedListener : nullptr); + } +} + +void WindowInfosListenerInvoker::windowInfosReported() { + mCallbacksPending--; + if (mCallbacksPending == 0) { + mSf->windowInfosReported(); + } +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h new file mode 100644 index 0000000000..5e5796fe2d --- /dev/null +++ b/services/surfaceflinger/WindowInfosListenerInvoker.h @@ -0,0 +1,57 @@ +/* + * 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 <android/gui/BnWindowInfosReportedListener.h> +#include <android/gui/IWindowInfosListener.h> +#include <android/gui/IWindowInfosReportedListener.h> +#include <binder/IBinder.h> +#include <utils/Mutex.h> +#include <unordered_map> + +namespace android { + +class SurfaceFlinger; + +class WindowInfosListenerInvoker : public IBinder::DeathRecipient { +public: + WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf); + void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener); + void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener); + + void windowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, bool shouldSync); + +protected: + void binderDied(const wp<IBinder>& who) override; + +private: + void windowInfosReported(); + + struct WpHash { + size_t operator()(const wp<IBinder>& p) const { + return std::hash<IBinder*>()(p.unsafe_get()); + } + }; + + const sp<SurfaceFlinger> mSf; + std::mutex mListenersMutex; + std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash> + mWindowInfosListeners GUARDED_BY(mListenersMutex); + sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener; + std::atomic<size_t> mCallbacksPending{0}; +}; +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp index c8a2b5eed6..973a4392a2 100644 --- a/services/surfaceflinger/layerproto/Android.bp +++ b/services/surfaceflinger/layerproto/Android.bp @@ -13,8 +13,7 @@ cc_library { srcs: [ "LayerProtoParser.cpp", - "layers.proto", - "layerstrace.proto", + "*.proto", ], shared_libs: [ diff --git a/services/surfaceflinger/layerproto/common.proto b/services/surfaceflinger/layerproto/common.proto new file mode 100644 index 0000000000..1c73a9f4a8 --- /dev/null +++ b/services/surfaceflinger/layerproto/common.proto @@ -0,0 +1,39 @@ +/* + * 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. + */ + +syntax = "proto3"; +option optimize_for = LITE_RUNTIME; +package android.surfaceflinger; + +message RectProto { + int32 left = 1; + int32 top = 2; + int32 right = 3; + int32 bottom = 4; +} + +message SizeProto { + int32 w = 1; + int32 h = 2; +} + +message TransformProto { + float dsdx = 1; + float dtdx = 2; + float dsdy = 3; + float dtdy = 4; + int32 type = 5; +}
\ No newline at end of file diff --git a/services/surfaceflinger/layerproto/display.proto b/services/surfaceflinger/layerproto/display.proto new file mode 100644 index 0000000000..ee8830e7f2 --- /dev/null +++ b/services/surfaceflinger/layerproto/display.proto @@ -0,0 +1,36 @@ +/* + * 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. + */ + +syntax = "proto3"; +option optimize_for = LITE_RUNTIME; + +import "frameworks/native/services/surfaceflinger/layerproto/common.proto"; + +package android.surfaceflinger; + +message DisplayProto { + uint64 id = 1; + + string name = 2; + + uint32 layer_stack = 3; + + SizeProto size = 4; + + RectProto layer_stack_space_rect = 5; + + TransformProto transform = 6; +} diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index dddc677715..057eabb8cc 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -2,6 +2,9 @@ syntax = "proto3"; option optimize_for = LITE_RUNTIME; + +import "frameworks/native/services/surfaceflinger/layerproto/common.proto"; + package android.surfaceflinger; // Contains a list of all layers. @@ -130,6 +133,9 @@ message LayerProto { repeated BlurRegion blur_regions = 54; bool is_trusted_overlay = 55; + + // Corner radius explicitly set on layer rather than inherited + float requested_corner_radius = 56; } message PositionProto { @@ -137,31 +143,11 @@ message PositionProto { float y = 2; } -message SizeProto { - int32 w = 1; - int32 h = 2; -} - -message TransformProto { - float dsdx = 1; - float dtdx = 2; - float dsdy = 3; - float dtdy = 4; - int32 type = 5; -} - message RegionProto { reserved 1; // Previously: uint64 id repeated RectProto rect = 2; } -message RectProto { - int32 left = 1; - int32 top = 2; - int32 right = 3; - int32 bottom = 4; -} - message FloatRectProto { float left = 1; float top = 2; @@ -228,4 +214,4 @@ message BlurRegion { int32 top = 8; int32 right = 9; int32 bottom = 10; -}
\ No newline at end of file +} diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto index 990f3cffda..13647b669e 100644 --- a/services/surfaceflinger/layerproto/layerstrace.proto +++ b/services/surfaceflinger/layerproto/layerstrace.proto @@ -18,6 +18,7 @@ syntax = "proto2"; option optimize_for = LITE_RUNTIME; import "frameworks/native/services/surfaceflinger/layerproto/layers.proto"; +import "frameworks/native/services/surfaceflinger/layerproto/display.proto"; package android.surfaceflinger; @@ -57,4 +58,6 @@ message LayersTraceProto { /* Number of missed entries since the last entry was recorded. */ optional uint32 missed_entries = 6; + + repeated DisplayProto displays = 7; } diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index a36c6ed521..32ad8732ad 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -51,6 +51,7 @@ cc_test { "Stress_test.cpp", "SurfaceInterceptor_test.cpp", "VirtualDisplay_test.cpp", + "WindowInfosListener_test.cpp", ], data: ["SurfaceFlinger_test.filter"], static_libs: [ diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp index af00ec7fc9..93656f3fd2 100644 --- a/services/surfaceflinger/tests/EffectLayer_test.cpp +++ b/services/surfaceflinger/tests/EffectLayer_test.cpp @@ -176,6 +176,15 @@ TEST_F(EffectLayerTest, BlurEffectLayerIsVisible) { } } +TEST_F(EffectLayerTest, EffectLayerWithColorNoCrop) { + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ui::DisplayMode mode; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); + const ui::Size& resolution = mode.resolution; + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, resolution.getWidth(), resolution.getHeight()), Color::RED); +} + } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp index 9fa3d4c417..84fea6c751 100644 --- a/services/surfaceflinger/tests/IPC_test.cpp +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -28,8 +28,8 @@ #include <limits> +#include <gui/test/CallbackUtils.h> #include "BufferGenerator.h" -#include "utils/CallbackUtils.h" #include "utils/ColorUtils.h" #include "utils/TransactionUtils.h" diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp index 9cf7c0909b..d192a2d83d 100644 --- a/services/surfaceflinger/tests/InvalidHandles_test.cpp +++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp @@ -52,17 +52,6 @@ protected: } }; -TEST_F(InvalidHandleTest, createSurfaceInvalidParentHandle) { - // The createSurface is scheduled now, we could still get a created surface from createSurface. - // Should verify if it actually added into current state by checking the screenshot. - auto notSc = mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0, - mNotSc->getHandle()); - LayerCaptureArgs args; - args.layerHandle = notSc->getHandle(); - ScreenCaptureResults captureResults; - ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults)); -} - TEST_F(InvalidHandleTest, captureLayersInvalidHandle) { LayerCaptureArgs args; args.layerHandle = mNotSc->getHandle(); diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp index 965aac301d..7ff041ea57 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -18,8 +18,8 @@ #include <gui/DisplayEventReceiver.h> +#include <gui/test/CallbackUtils.h> #include "LayerTransactionTest.h" -#include "utils/CallbackUtils.h" using namespace std::chrono_literals; diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index 43d957cf7a..25f3bb91ae 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -208,6 +208,37 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) { } } +// b/200781179 - don't round a layer without a valid crop +// This behaviour should be fixed since we treat buffer layers differently than +// effect or container layers. +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusInvalidCrop) { + sp<SurfaceControl> parent; + sp<SurfaceControl> child; + const uint8_t size = 64; + const uint8_t testArea = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::GREEN, size, size)); + ASSERT_NO_FATAL_FAILURE(child = createColorLayer("child", Color::RED)); + + Transaction().setCornerRadius(child, cornerRadius).reparent(child, parent).show(child).apply(); + { + const uint8_t bottom = size - 1; + const uint8_t right = size - 1; + auto shot = getScreenCapture(); + std::this_thread::sleep_for(std::chrono::seconds(5)); + // Solid corners since we don't round a layer without a valid crop + shot->expectColor(Rect(0, 0, testArea, testArea), Color::RED); + shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::RED); + shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::RED); + shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::RED); + // Solid center + shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2, + size / 2 + testArea / 2, size / 2 + testArea / 2), + Color::RED); + } +} + TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) { sp<SurfaceControl> parent; sp<SurfaceControl> child; @@ -465,6 +496,95 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildBufferRotation } } +TEST_P(LayerTypeAndRenderTypeTransactionTest, ChildCornerRadiusTakesPrecedence) { + sp<SurfaceControl> parent; + sp<SurfaceControl> child; + const uint32_t size = 64; + const uint32_t parentSize = size * 3; + const uint32_t testLength = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize)); + ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size)); + + Transaction() + .setCornerRadius(parent, cornerRadius) + .setCornerRadius(child, cornerRadius) + .reparent(child, parent) + .setPosition(child, size, size) + .apply(); + + { + const uint32_t top = size - 1; + const uint32_t left = size - 1; + const uint32_t bottom = size * 2 - 1; + const uint32_t right = size * 2 - 1; + auto shot = getScreenCapture(); + // Edges are transparent + // TL + shot->expectColor(Rect(left, top, testLength, testLength), Color::RED); + // TR + shot->expectColor(Rect(right - testLength, top, right, testLength), Color::RED); + // BL + shot->expectColor(Rect(left, bottom - testLength, testLength, bottom - testLength), + Color::RED); + // BR + shot->expectColor(Rect(right - testLength, bottom - testLength, right, bottom), Color::RED); + // Solid center + shot->expectColor(Rect(parentSize / 2 - testLength / 2, parentSize / 2 - testLength / 2, + parentSize / 2 + testLength / 2, parentSize / 2 + testLength / 2), + Color::GREEN); + } +} + +// Test if ParentCornerRadiusTakesPrecedence if the parent corner radius crop is fully contained by +// the child corner radius crop. +TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusTakesPrecedence) { + sp<SurfaceControl> parent; + sp<SurfaceControl> child; + const uint32_t size = 64; + const uint32_t parentSize = size * 3; + const Rect parentCrop(size + 1, size + 1, size * 2 - 1, size * 2 - 1); + const uint32_t testLength = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize)); + ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size)); + + Transaction() + .setCornerRadius(parent, cornerRadius) + .setCrop(parent, parentCrop) + .setCornerRadius(child, cornerRadius) + .reparent(child, parent) + .setPosition(child, size, size) + .apply(); + + { + const uint32_t top = size - 1; + const uint32_t left = size - 1; + const uint32_t bottom = size * 2 - 1; + const uint32_t right = size * 2 - 1; + auto shot = getScreenCapture(); + // Edges are transparent + // TL + shot->expectColor(Rect(left, top, testLength, testLength), Color::BLACK); + // TR + shot->expectColor(Rect(right - testLength, top, right, testLength), Color::BLACK); + // BL + shot->expectColor(Rect(left, bottom - testLength, testLength, bottom - testLength), + Color::BLACK); + // BR + shot->expectColor(Rect(right - testLength, bottom - testLength, right, bottom), + Color::BLACK); + // Solid center + shot->expectColor(Rect(parentSize / 2 - testLength / 2, parentSize / 2 - testLength / 2, + parentSize / 2 + testLength / 2, parentSize / 2 + testLength / 2), + Color::GREEN); + } +} + TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) { if (!deviceSupportsBlurs()) GTEST_SKIP(); if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP(); diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index d02786504e..b7a927129d 100644 --- a/services/surfaceflinger/tests/MirrorLayer_test.cpp +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -273,6 +273,61 @@ TEST_F(MirrorLayerTest, InitialMirrorState) { } } +// Test that a mirror layer can be screenshot when offscreen +TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) { + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ui::DisplayMode mode; + SurfaceComposerClient::getActiveDisplayMode(display, &mode); + const ui::Size& size = mode.resolution; + + sp<SurfaceControl> grandchild = + createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState, + mChildLayer.get()); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50)); + Rect childBounds = Rect(50, 50, 450, 450); + + asTransaction([&](Transaction& t) { + t.setCrop(grandchild, Rect(0, 0, 50, 50)).show(grandchild); + t.setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); + }); + + sp<SurfaceControl> mirrorLayer = nullptr; + { + // Run as system to get the ACCESS_SURFACE_FLINGER permission when mirroring + UIDFaker f(AID_SYSTEM); + // Mirror mChildLayer + mirrorLayer = mClient->mirrorSurface(mChildLayer.get()); + ASSERT_NE(mirrorLayer, nullptr); + } + + // Show the mirror layer, but don't reparent to a layer on screen. + Transaction().show(mirrorLayer).apply(); + + { + SCOPED_TRACE("Offscreen Mirror"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, size.getWidth(), 50), Color::RED); + shot->expectColor(Rect(0, 0, 50, size.getHeight()), Color::RED); + shot->expectColor(Rect(450, 0, size.getWidth(), size.getHeight()), Color::RED); + shot->expectColor(Rect(0, 450, size.getWidth(), size.getHeight()), Color::RED); + shot->expectColor(Rect(100, 100, 450, 450), Color::GREEN); + shot->expectColor(Rect(50, 50, 100, 100), Color::BLUE); + } + + { + SCOPED_TRACE("Capture Mirror"); + // Capture just the mirror layer and child. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mirrorLayer->getHandle(); + captureArgs.sourceCrop = childBounds; + std::unique_ptr<ScreenCapture> shot; + ScreenCapture::captureLayers(&shot, captureArgs); + shot->expectSize(childBounds.width(), childBounds.height()); + shot->expectColor(Rect(0, 0, 50, 50), Color::BLUE); + shot->expectColor(Rect(50, 50, 400, 400), Color::GREEN); + } +} + } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp index 05858bf839..fb4458a27e 100644 --- a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp +++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp @@ -20,6 +20,10 @@ #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> +#include <chrono> + +using ::std::literals::chrono_literals::operator""ms; +using ::std::literals::chrono_literals::operator""s; static constexpr int kRefreshRateOverlayCode = 1034; static constexpr int kRefreshRateOverlayEnable = 1; diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index 579a26ebf4..309ab2f0db 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ +#include <gui/test/CallbackUtils.h> #include "LayerTransactionTest.h" -#include "utils/CallbackUtils.h" using namespace std::chrono_literals; @@ -61,7 +61,7 @@ public: std::this_thread::sleep_for(300ms); std::lock_guard lock(mMutex); - EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; + EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received"; mCallbackDataQueue = {}; } @@ -99,10 +99,10 @@ public: } static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback, - const ReleaseCallbackId& expectedCallbackId) { + const ReleaseCallbackId& expectedReleaseBufferId) { ReleaseCallbackId actualReleaseBufferId; releaseCallback.getCallbackData(&actualReleaseBufferId); - EXPECT_EQ(expectedCallbackId, actualReleaseBufferId); + EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId); releaseCallback.verifyNoCallbacks(); } static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() { @@ -333,4 +333,60 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } +TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { + sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener(); + sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener(); + + CallbackHelper callback1, callback2; + + TransactionCompletedListener::setInstance(firstCompletedListener); + + sp<SurfaceControl> layer = createBufferStateLayer(); + ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); + + sp<GraphicBuffer> firstBuffer = getBuffer(); + ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); + + // Send initial buffer for the layer + submitBuffer(layer, firstBuffer, Fence::NO_FENCE, callback1, firstBufferCallbackId, + *releaseCallback); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED); + ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); + + // Sent a second buffer to allow the first buffer to get released. + sp<GraphicBuffer> secondBuffer = getBuffer(); + ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); + + Transaction transaction1; + transaction1.setFrameNumber(layer, secondBufferCallbackId.framenumber); + transaction1.setBuffer(layer, secondBuffer, secondBufferCallbackId, + releaseCallback->getCallback()); + transaction1.setAcquireFence(layer, Fence::NO_FENCE); + transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext()); + + // Set a different TransactionCompletedListener to mimic a second process + TransactionCompletedListener::setInstance(secondCompletedListener); + + // Make sure the second "process" has a callback set up. + Transaction transaction2; + transaction2.addTransactionCompletedCallback(callback2.function, callback2.getContext()); + + // This merging order, merge transaction1 first then transaction2, seems to ensure the listener + // for transaction2 is ordered first. This makes sure the wrong process is added first to the + // layer's vector of listeners. With the bug, only the secondCompletedListener will get the + // release callback id, since it's ordered first. Then firstCompletedListener would fail to get + // the release callback id and not invoke the release callback. + Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply(); + + expected = ExpectedResult(); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED, + ExpectedResult::PreviousBuffer::RELEASED); + ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); + ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); +} + } // namespace android diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index 6912fcf219..2d5b50238c 100644 --- a/services/surfaceflinger/tests/ScreenCapture_test.cpp +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -37,6 +37,8 @@ protected: ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); const ui::Size& resolution = mode.resolution; + mDisplaySize = resolution; + // Background surface mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(), resolution.getHeight(), 0); @@ -72,6 +74,7 @@ protected: sp<SurfaceControl> mBGSurfaceControl; sp<SurfaceControl> mFGSurfaceControl; std::unique_ptr<ScreenCapture> mCapture; + ui::Size mDisplaySize; }; TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { @@ -515,21 +518,27 @@ TEST_F(ScreenCaptureTest, CaptureSize) { } TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + LayerCaptureArgs args; + args.layerHandle = new BBinder(); + ScreenCaptureResults captureResults; + // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND + ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults)); +} + +TEST_F(ScreenCaptureTest, CaptureTooLargeLayer) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60); ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); - auto redLayerHandle = redLayer->getHandle(); - Transaction().reparent(redLayer, nullptr).apply(); - redLayer.clear(); - SurfaceComposerClient::Transaction().apply(true); + Transaction().show(redLayer).setLayer(redLayer, INT32_MAX).apply(true); - LayerCaptureArgs args; - args.layerHandle = redLayerHandle; + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = redLayer->getHandle(); + captureArgs.frameScaleX = INT32_MAX / 60; + captureArgs.frameScaleY = INT32_MAX / 60; ScreenCaptureResults captureResults; - // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND - ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults)); + ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(captureArgs, captureResults)); } TEST_F(ScreenCaptureTest, CaptureSecureLayer) { @@ -824,6 +833,33 @@ TEST_F(ScreenCaptureTest, CaptureWithGrayscale) { Color{expectedColor, expectedColor, expectedColor, 255}, tolerance); } +TEST_F(ScreenCaptureTest, CaptureOffscreen) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, + ISurfaceComposerClient::eFXSurfaceBufferState, + mBGSurfaceControl.get())); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply(); + + DisplayCaptureArgs displayCaptureArgs; + displayCaptureArgs.displayToken = mDisplay; + + { + // Validate that the red layer is not on screen + ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs); + mCapture->expectColor(Rect(0, 0, mDisplaySize.width, mDisplaySize.height), + {63, 63, 195, 255}); + } + + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = layer->getHandle(); + + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectSize(32, 32); + mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED); +} + // In the following tests we verify successful skipping of a parent layer, // so we use the same verification logic and only change how we mutate // the parent layer to verify that various properties are ignored. diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index d5890ffa79..a42405989f 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -61,7 +61,7 @@ constexpr auto UNIQUE_TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface#0"; constexpr auto LAYER_NAME = "Layer Create and Delete Test"; constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0"; -constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb"; +constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope"; // Fill an RGBA_8888 formatted surface with a single color. static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) { diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp new file mode 100644 index 0000000000..15b79be06c --- /dev/null +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -0,0 +1,137 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gui/SurfaceComposerClient.h> +#include <private/android_filesystem_config.h> +#include <future> +#include "utils/TransactionUtils.h" + +namespace android { +using Transaction = SurfaceComposerClient::Transaction; +using gui::WindowInfo; + +class WindowInfosListenerTest : public ::testing::Test { +protected: + void SetUp() override { + seteuid(AID_SYSTEM); + mClient = new SurfaceComposerClient; + mWindowInfosListener = new SyncWindowInfosListener(); + mClient->addWindowInfosListener(mWindowInfosListener); + } + + void TearDown() override { + mClient->removeWindowInfosListener(mWindowInfosListener); + seteuid(AID_ROOT); + } + + struct SyncWindowInfosListener : public gui::WindowInfosListener { + public: + void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override { + windowInfosPromise.set_value(windowInfos); + } + + std::vector<WindowInfo> waitForWindowInfos() { + std::future<std::vector<WindowInfo>> windowInfosFuture = + windowInfosPromise.get_future(); + std::vector<WindowInfo> windowInfos = windowInfosFuture.get(); + windowInfosPromise = std::promise<std::vector<WindowInfo>>(); + return windowInfos; + } + + private: + std::promise<std::vector<WindowInfo>> windowInfosPromise; + }; + + sp<SurfaceComposerClient> mClient; + sp<SyncWindowInfosListener> mWindowInfosListener; +}; + +std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo, + std::vector<WindowInfo> windowInfos) { + std::optional<WindowInfo> foundWindowInfo = std::nullopt; + for (WindowInfo windowInfo : windowInfos) { + if (windowInfo.token == targetWindowInfo.token) { + foundWindowInfo = std::make_optional<>(windowInfo); + break; + } + } + + return foundWindowInfo; +} + +TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { + std::string name = "Test Layer"; + sp<IBinder> token = new BBinder(); + WindowInfo windowInfo; + windowInfo.name = name; + windowInfo.token = token; + sp<SurfaceControl> surfaceControl = + mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + + Transaction() + .setLayerStack(surfaceControl, 0) + .show(surfaceControl) + .setLayer(surfaceControl, INT32_MAX - 1) + .setInputWindowInfo(surfaceControl, windowInfo) + .apply(); + + std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos(); + std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + ASSERT_NE(std::nullopt, foundWindowInfo); + + Transaction().reparent(surfaceControl, nullptr).apply(); + + windowInfos = mWindowInfosListener->waitForWindowInfos(); + foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + ASSERT_EQ(std::nullopt, foundWindowInfo); +} + +TEST_F(WindowInfosListenerTest, WindowInfoChanged) { + std::string name = "Test Layer"; + sp<IBinder> token = new BBinder(); + WindowInfo windowInfo; + windowInfo.name = name; + windowInfo.token = token; + sp<SurfaceControl> surfaceControl = + mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + const Rect crop(0, 0, 100, 100); + Transaction() + .setLayerStack(surfaceControl, 0) + .show(surfaceControl) + .setLayer(surfaceControl, INT32_MAX - 1) + .setCrop(surfaceControl, crop) + .setInputWindowInfo(surfaceControl, windowInfo) + .apply(); + + std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos(); + std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + ASSERT_NE(std::nullopt, foundWindowInfo); + ASSERT_TRUE(foundWindowInfo->touchableRegion.isEmpty()); + + Rect touchableRegions(0, 0, 50, 50); + windowInfo.addTouchableRegion(Rect(0, 0, 50, 50)); + Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply(); + + windowInfos = mWindowInfosListener->waitForWindowInfos(); + foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + ASSERT_NE(std::nullopt, foundWindowInfo); + ASSERT_TRUE(foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion)); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index b5086fafb7..9e704c32fc 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -51,6 +51,7 @@ cc_test { "DisplayIdGeneratorTest.cpp", "DisplayTransactionTest.cpp", "DisplayDevice_GetBestColorModeTest.cpp", + "DisplayDevice_InitiateModeChange.cpp", "DisplayDevice_SetProjectionTest.cpp", "EventThreadTest.cpp", "FpsReporterTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 560f139719..52a36a2719 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -108,6 +108,8 @@ public: mComposer = new Hwc2::mock::Composer(); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + + mFlinger.mutableMaxRenderTargetSize() = 16384; } ~CompositionTest() { @@ -519,8 +521,6 @@ struct BaseLayerProperties { static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) { // TODO: Eliminate the complexity of actually creating a buffer - EXPECT_CALL(*test->mRenderEngine, getMaxTextureSize()).WillOnce(Return(16384)); - EXPECT_CALL(*test->mRenderEngine, getMaxViewportDims()).WillOnce(Return(16384)); status_t err = layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT, LayerProperties::FORMAT); diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp new file mode 100644 index 0000000000..d4cfbbbe0c --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include "DisplayTransactionTestHelpers.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { +namespace { + +using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + +class InitiateModeChangeTest : public DisplayTransactionTest { +public: + using Event = scheduler::RefreshRateConfigEvent; + + void SetUp() override { + injectFakeBufferQueueFactory(); + injectFakeNativeWindowSurfaceFactory(); + + PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this); + PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this); + PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this); + PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this); + PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this); + + mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); + + mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) + .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120}) + .setActiveMode(kDisplayModeId60) + .inject(); + } + +protected: + sp<DisplayDevice> mDisplay; + + const DisplayModeId kDisplayModeId60 = DisplayModeId(0); + const DisplayModePtr kDisplayMode60 = + DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value())) + .setId(kDisplayModeId60) + .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()) + .setVsyncPeriod(int32_t(16'666'667)) + .setGroup(0) + .setHeight(1000) + .setWidth(1000) + .build(); + + const DisplayModeId kDisplayModeId90 = DisplayModeId(1); + const DisplayModePtr kDisplayMode90 = + DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value())) + .setId(kDisplayModeId90) + .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()) + .setVsyncPeriod(int32_t(11'111'111)) + .setGroup(0) + .setHeight(1000) + .setWidth(1000) + .build(); + + const DisplayModeId kDisplayModeId120 = DisplayModeId(2); + const DisplayModePtr kDisplayMode120 = + DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value())) + .setId(kDisplayModeId120) + .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()) + .setVsyncPeriod(int32_t(8'333'333)) + .setGroup(0) + .setHeight(1000) + .setWidth(1000) + .build(); +}; + +TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) { + EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode60, Event::None})); + EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); +} + +TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) { + EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None})); + ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); + EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); + + // Setting another mode should be cached but return false + EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode120, Event::None})); + ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); + EXPECT_EQ(kDisplayMode120, mDisplay->getDesiredActiveMode()->mode); + EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); +} + +TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) { + EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None})); + ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); + + mDisplay->clearDesiredActiveModeState(); + ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); +} + +TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS { + EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None})); + ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); + EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); + + hal::VsyncPeriodChangeConstraints constraints{ + .desiredTimeNanos = systemTime(), + .seamlessRequired = false, + }; + hal::VsyncPeriodChangeTimeline timeline; + EXPECT_EQ(OK, + mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, + &timeline)); + EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); + + mDisplay->clearDesiredActiveModeState(); + ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); +} + +TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged) +NO_THREAD_SAFETY_ANALYSIS { + EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None})); + ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); + EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); + + hal::VsyncPeriodChangeConstraints constraints{ + .desiredTimeNanos = systemTime(), + .seamlessRequired = false, + }; + hal::VsyncPeriodChangeTimeline timeline; + EXPECT_EQ(OK, + mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, + &timeline)); + EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); + + EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode120, Event::None})); + ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); + EXPECT_EQ(kDisplayMode120, mDisplay->getDesiredActiveMode()->mode); + EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); + + EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); + + EXPECT_EQ(OK, + mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, + &timeline)); + EXPECT_EQ(kDisplayMode120, mDisplay->getUpcomingActiveMode().mode); + EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); + + mDisplay->clearDesiredActiveModeState(); + ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 6ce281d403..de058a4f68 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -615,8 +615,8 @@ struct HwcVirtualDisplayVariant } static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _, _)) - .WillOnce(DoAll(SetArgPointee<4>(Self::HWC_DISPLAY_ID), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _)) + .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE))); EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE)); } }; diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index b4a1481e9c..4ff7592b71 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -543,17 +543,34 @@ TEST_F(EventThreadTest, postHotplugExternalConnect) { } TEST_F(EventThreadTest, postConfigChangedPrimary) { - mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(7), 16666666); + const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) + .setPhysicalDisplayId(INTERNAL_DISPLAY_ID) + .setId(DisplayModeId(7)) + .setVsyncPeriod(16666666) + .build(); + + mThread->onModeChanged(mode); expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666); } TEST_F(EventThreadTest, postConfigChangedExternal) { - mThread->onModeChanged(EXTERNAL_DISPLAY_ID, DisplayModeId(5), 16666666); + const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) + .setPhysicalDisplayId(EXTERNAL_DISPLAY_ID) + .setId(DisplayModeId(5)) + .setVsyncPeriod(16666666) + .build(); + + mThread->onModeChanged(mode); expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666); } TEST_F(EventThreadTest, postConfigChangedPrimary64bit) { - mThread->onModeChanged(DISPLAY_ID_64BIT, DisplayModeId(7), 16666666); + const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) + .setPhysicalDisplayId(DISPLAY_ID_64BIT) + .setId(DisplayModeId(7)) + .setVsyncPeriod(16666666) + .build(); + mThread->onModeChanged(mode); expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666); } @@ -562,7 +579,13 @@ TEST_F(EventThreadTest, suppressConfigChanged) { sp<MockEventThreadConnection> suppressConnection = createConnection(suppressConnectionEventRecorder); - mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(9), 16666666); + const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) + .setPhysicalDisplayId(INTERNAL_DISPLAY_ID) + .setId(DisplayModeId(9)) + .setVsyncPeriod(16666666) + .build(); + + mThread->onModeChanged(mode); expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666); auto args = suppressConnectionEventRecorder.waitForCall(); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index b67ebcaa49..02ec7fc493 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -35,6 +35,7 @@ using testing::_; using testing::Return; +using testing::ReturnRef; namespace android { @@ -62,6 +63,10 @@ protected: LayerHistory& history() { return *mScheduler->mutableLayerHistory(); } const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); } + LayerHistory::Summary summarizeLayerHistory(nsecs_t now) { + return history().summarize(*mScheduler->refreshRateConfigs(), now); + } + size_t layerCount() const { return mScheduler->layerHistorySize(); } size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; } @@ -101,7 +106,7 @@ protected: history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += frameRate.getPeriodNsecs(); - summary = history().summarize(time); + summary = summarizeLayerHistory(time); } ASSERT_EQ(1, summary.size()); @@ -110,21 +115,24 @@ protected: << "Frame rate is " << frameRate; } - RefreshRateConfigs mConfigs{{DisplayMode::Builder(0) - .setId(DisplayModeId(0)) - .setVsyncPeriod(int32_t(LO_FPS_PERIOD)) - .setGroup(0) - .build(), - DisplayMode::Builder(1) - .setId(DisplayModeId(1)) - .setVsyncPeriod(int32_t(HI_FPS_PERIOD)) - .setGroup(0) - .build()}, - DisplayModeId(0)}; - - mock::NoOpSchedulerCallback mSchedulerCallback; - - TestableScheduler* const mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback); + std::shared_ptr<RefreshRateConfigs> mConfigs = std::make_shared< + RefreshRateConfigs>(DisplayModes{DisplayMode::Builder(0) + .setId(DisplayModeId(0)) + .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setVsyncPeriod(int32_t(LO_FPS_PERIOD)) + .setGroup(0) + .build(), + DisplayMode::Builder(1) + .setId(DisplayModeId(1)) + .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setVsyncPeriod(int32_t(HI_FPS_PERIOD)) + .setGroup(0) + .build()}, + DisplayModeId(0)); + + mock::SchedulerCallback mSchedulerCallback; + + TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback); TestableSurfaceFlinger mFlinger; }; @@ -142,22 +150,22 @@ TEST_F(LayerHistoryTest, oneLayer) { const nsecs_t time = systemTime(); // No layers returned if no layers are active. - EXPECT_TRUE(history().summarize(time).empty()); + EXPECT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(0, activeLayerCount()); // Max returned if active layers have insufficient history. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); } // Max is returned since we have enough history but there is no timestamp votes. for (int i = 0; i < 10; i++) { history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); } } @@ -173,17 +181,17 @@ TEST_F(LayerHistoryTest, oneInvisibleLayer) { nsecs_t time = systemTime(); history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); - auto summary = history().summarize(time); - ASSERT_EQ(1, history().summarize(time).size()); + auto summary = summarizeLayerHistory(time); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); // Layer is still considered inactive so we expect to get Min - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false)); history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); - summary = history().summarize(time); - EXPECT_TRUE(history().summarize(time).empty()); + summary = summarizeLayerHistory(time); + EXPECT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(0, activeLayerCount()); } @@ -201,9 +209,9 @@ TEST_F(LayerHistoryTest, explicitTimestamp) { time += LO_FPS_PERIOD; } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_TRUE(LO_FPS.equalsWithMargin(history().summarize(time)[0].desiredRefreshRate)); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); + EXPECT_TRUE(LO_FPS.equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate)); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); } @@ -224,13 +232,13 @@ TEST_F(LayerHistoryTest, oneLayerNoVote) { time += HI_FPS_PERIOD; } - ASSERT_TRUE(history().summarize(time).empty()); + ASSERT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer became inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_TRUE(history().summarize(time).empty()); + ASSERT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); } @@ -251,14 +259,14 @@ TEST_F(LayerHistoryTest, oneLayerMinVote) { time += HI_FPS_PERIOD; } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer became inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_TRUE(history().summarize(time).empty()); + ASSERT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); } @@ -279,14 +287,14 @@ TEST_F(LayerHistoryTest, oneLayerMaxVote) { time += LO_FPS_PERIOD; } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer became inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_TRUE(history().summarize(time).empty()); + ASSERT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); } @@ -307,18 +315,18 @@ TEST_F(LayerHistoryTest, oneLayerExplicitVote) { time += HI_FPS_PERIOD; } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote); - EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate)); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote); + EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate)); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer became inactive, but the vote stays setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote); - EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate)); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote); + EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate)); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); } @@ -339,20 +347,20 @@ TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) { time += HI_FPS_PERIOD; } - ASSERT_EQ(1, history().summarize(time).size()); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, - history().summarize(time)[0].vote); - EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate)); + summarizeLayerHistory(time)[0].vote); + EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate)); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer became inactive, but the vote stays setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, history().summarize(time).size()); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, - history().summarize(time)[0].vote); - EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate)); + summarizeLayerHistory(time)[0].vote); + EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate)); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); } @@ -383,7 +391,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - summary = history().summarize(time); + summary = summarizeLayerHistory(time); } ASSERT_EQ(1, summary.size()); @@ -395,7 +403,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; - summary = history().summarize(time); + summary = summarizeLayerHistory(time); } // layer1 is still active but infrequent. @@ -404,7 +412,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { ASSERT_EQ(2, summary.size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote); ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote); - EXPECT_TRUE(HI_FPS.equalsWithMargin(history().summarize(time)[1].desiredRefreshRate)); + EXPECT_TRUE(HI_FPS.equalsWithMargin(summarizeLayerHistory(time)[1].desiredRefreshRate)); EXPECT_EQ(2, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); @@ -414,7 +422,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; - summary = history().summarize(time); + summary = summarizeLayerHistory(time); } ASSERT_EQ(1, summary.size()); @@ -433,7 +441,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; - summary = history().summarize(time); + summary = summarizeLayerHistory(time); } ASSERT_EQ(2, summary.size()); @@ -445,7 +453,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer3 becomes recently active. history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); - summary = history().summarize(time); + summary = summarizeLayerHistory(time); ASSERT_EQ(2, summary.size()); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate)); @@ -456,7 +464,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer1 expires. layer1.clear(); - summary = history().summarize(time); + summary = summarizeLayerHistory(time); ASSERT_EQ(2, summary.size()); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); @@ -472,7 +480,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; - summary = history().summarize(time); + summary = summarizeLayerHistory(time); } ASSERT_EQ(1, summary.size()); @@ -483,7 +491,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer2 expires. layer2.clear(); - summary = history().summarize(time); + summary = summarizeLayerHistory(time); EXPECT_TRUE(summary.empty()); EXPECT_EQ(1, layerCount()); EXPECT_EQ(0, activeLayerCount()); @@ -493,7 +501,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) { history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; - summary = history().summarize(time); + summary = summarizeLayerHistory(time); } ASSERT_EQ(1, summary.size()); @@ -505,7 +513,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer3 expires. layer3.clear(); - summary = history().summarize(time); + summary = summarizeLayerHistory(time); EXPECT_TRUE(summary.empty()); EXPECT_EQ(0, layerCount()); EXPECT_EQ(0, activeLayerCount()); @@ -526,8 +534,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); } @@ -537,8 +545,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); @@ -551,8 +559,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { time += HI_FPS_PERIOD; EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); } @@ -562,8 +570,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { time += HI_FPS_PERIOD; EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); } @@ -590,10 +598,10 @@ TEST_F(LayerHistoryTest, invisibleExplicitLayer) { LayerHistory::LayerUpdateType::Buffer); EXPECT_EQ(2, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, - history().summarize(time)[0].vote); - EXPECT_TRUE(Fps(60.0f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate)); + summarizeLayerHistory(time)[0].vote); + EXPECT_TRUE(Fps(60.0f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate)); EXPECT_EQ(2, activeLayerCount()); EXPECT_EQ(2, frequentLayerCount(time)); } @@ -617,8 +625,8 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); @@ -627,8 +635,8 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); @@ -637,8 +645,8 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(1, animatingLayerCount(time)); @@ -727,13 +735,13 @@ TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) { } if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) { - ASSERT_NE(0, history().summarize(time).size()); - ASSERT_GE(2, history().summarize(time).size()); + ASSERT_NE(0, summarizeLayerHistory(time).size()); + ASSERT_GE(2, summarizeLayerHistory(time).size()); bool max = false; bool min = false; Fps heuristic{0.0}; - for (const auto& layer : history().summarize(time)) { + for (const auto& layer : summarizeLayerHistory(time)) { if (layer.vote == LayerHistory::LayerVoteType::Heuristic) { heuristic = layer.desiredRefreshRate; } else if (layer.vote == LayerHistory::LayerVoteType::Max) { @@ -746,7 +754,7 @@ TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) { if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) { EXPECT_TRUE(Fps(24.0f).equalsWithMargin(heuristic)); EXPECT_FALSE(max); - if (history().summarize(time).size() == 2) { + if (summarizeLayerHistory(time).size() == 2) { EXPECT_TRUE(min); } } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 112fabdefe..77b75ab767 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -51,8 +51,7 @@ protected: ~RefreshRateConfigsTest(); RefreshRate createRefreshRate(DisplayModePtr displayMode) { - return {displayMode->getId(), displayMode, displayMode->getFps(), - RefreshRate::ConstructorTag(0)}; + return {displayMode, RefreshRate::ConstructorTag(0)}; } Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) { @@ -164,24 +163,17 @@ protected: mConfig60, mConfig60Frac}; // Expected RefreshRate objects - RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, Fps(60), - RefreshRate::ConstructorTag(0)}; - RefreshRate mExpectedAlmost60Config = {HWC_CONFIG_ID_60, - createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665), - Fps(60), RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected90Config = {HWC_CONFIG_ID_90, mConfig90, Fps(90), - RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected90DifferentGroupConfig = {HWC_CONFIG_ID_90, mConfig90DifferentGroup, - Fps(90), RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected90DifferentResolutionConfig = {HWC_CONFIG_ID_90, - mConfig90DifferentResolution, Fps(90), + RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)}; + RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665), + RefreshRate::ConstructorTag(0)}; + RefreshRate mExpected90Config = {mConfig90, RefreshRate::ConstructorTag(0)}; + RefreshRate mExpected90DifferentGroupConfig = {mConfig90DifferentGroup, + RefreshRate::ConstructorTag(0)}; + RefreshRate mExpected90DifferentResolutionConfig = {mConfig90DifferentResolution, RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected72Config = {HWC_CONFIG_ID_72, mConfig72, Fps(72.0f), - RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected30Config = {HWC_CONFIG_ID_30, mConfig30, Fps(30), - RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120), - RefreshRate::ConstructorTag(0)}; + RefreshRate mExpected72Config = {mConfig72, RefreshRate::ConstructorTag(0)}; + RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)}; + RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)}; DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod, ui::Size resolution = ui::Size()); @@ -205,6 +197,7 @@ DisplayModePtr RefreshRateConfigsTest::createDisplayMode(DisplayModeId modeId, i int64_t vsyncPeriod, ui::Size resolution) { return DisplayMode::Builder(hal::HWConfigId(modeId.value())) .setId(modeId) + .setPhysicalDisplayId(PhysicalDisplayId(0)) .setVsyncPeriod(int32_t(vsyncPeriod)) .setGroup(group) .setHeight(resolution.height) @@ -2515,26 +2508,6 @@ TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) { ASSERT_TRUE(frameRateOverrides.empty()); } -TEST_F(RefreshRateConfigsTest, updateDisplayModes) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_30); - refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(HWC_CONFIG_ID_30), - /* allowGroupSwitching */ false, - /* range */ {Fps(30.0f), Fps(30.0f)}}); - - refreshRateConfigs->updateDisplayModes(m60_90Device, HWC_CONFIG_ID_60); - - const auto currentRefreshRate = refreshRateConfigs->getCurrentRefreshRate(); - EXPECT_TRUE(currentRefreshRate.getFps().equalsWithMargin(Fps(60.0))); - EXPECT_EQ(currentRefreshRate.getModeId(), HWC_CONFIG_ID_60); - - EXPECT_TRUE( - getMaxSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(90.0))); - EXPECT_TRUE( - getMinSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(60.0))); -} - } // namespace } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index bf07106afd..12b155bd2c 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -81,6 +81,7 @@ DisplayModePtr RefreshRateStatsTest::createDisplayMode(DisplayModeId modeId, int int64_t vsyncPeriod) { return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value())) .setId(modeId) + .setPhysicalDisplayId(PhysicalDisplayId(0)) .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod)) .setGroup(group) .build(); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index f680d802e6..5713c2f64a 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -53,16 +53,19 @@ protected: const DisplayModePtr mode60 = DisplayMode::Builder(0) .setId(DisplayModeId(0)) + .setPhysicalDisplayId(PhysicalDisplayId(0)) .setVsyncPeriod(Fps(60.f).getPeriodNsecs()) .setGroup(0) .build(); const DisplayModePtr mode120 = DisplayMode::Builder(1) .setId(DisplayModeId(1)) + .setPhysicalDisplayId(PhysicalDisplayId(0)) .setVsyncPeriod(Fps(120.f).getPeriodNsecs()) .setGroup(0) .build(); - scheduler::RefreshRateConfigs mConfigs{{mode60}, mode60->getId()}; + std::shared_ptr<scheduler::RefreshRateConfigs> mConfigs = + std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId()); mock::SchedulerCallback mSchedulerCallback; @@ -171,7 +174,7 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup mScheduler->setDisplayPowerState(kPowerStateNormal); constexpr uint32_t kDisplayArea = 999'999; - mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea); + mScheduler->onActiveDisplayAreaChanged(kDisplayArea); EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0); mScheduler->chooseRefreshRateForContent(); @@ -182,7 +185,9 @@ TEST_F(SchedulerTest, updateDisplayModes) { sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger()); ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize()); - mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId()); + mScheduler->setRefreshRateConfigs( + std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120}, + mode60->getId())); ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); @@ -194,20 +199,21 @@ TEST_F(SchedulerTest, testDispatchCachedReportedMode) { // onModeChange is called. mScheduler->clearOptionalFieldsInFeatures(); EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode()); - EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0); + EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); } TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) { - DisplayModeId modeId = DisplayModeId(111); - nsecs_t vsyncPeriod = 111111; + const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) + .setId(DisplayModeId(111)) + .setPhysicalDisplayId(PHYSICAL_DISPLAY_ID) + .setVsyncPeriod(111111) + .build(); // If the handle is incorrect, the function should return before // onModeChange is called. Scheduler::ConnectionHandle invalidHandle = {.id = 123}; - EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, - PHYSICAL_DISPLAY_ID, modeId, - vsyncPeriod)); - EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0); + EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode)); + EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); } TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { @@ -225,7 +231,9 @@ MATCHER(Is120Hz, "") { } TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { - mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId()); + mScheduler->setRefreshRateConfigs( + std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120}, + mode60->getId())); sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger()); @@ -235,7 +243,7 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { mScheduler->setDisplayPowerState(kPowerStateNormal); constexpr uint32_t kDisplayArea = 999'999; - mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea); + mScheduler->onActiveDisplayAreaChanged(kDisplayArea); EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1); mScheduler->chooseRefreshRateForContent(); diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index 2761470c31..a4e9d20b75 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -479,7 +479,9 @@ TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); const auto layerHistorySummary = - mFlinger.mutableScheduler().mutableLayerHistory()->summarize(0); + mFlinger.mutableScheduler() + .mutableLayerHistory() + ->summarize(*mFlinger.mutableScheduler().refreshRateConfigs(), 0); ASSERT_EQ(2u, layerHistorySummary.size()); EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[0].desiredRefreshRate)); EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[1].desiredRefreshRate)); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp index be019848fb..fc40818ad1 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp @@ -224,6 +224,74 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStac EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack); } +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfFlagsNotChanged) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display has flags set + display.mutableCurrentDisplayState().flags = 1u; + + // The incoming request sets a different layer stack + DisplayState state; + state.what = DisplayState::eFlagsChanged; + state.token = display.token(); + state.flags = 1u; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags are empty + EXPECT_EQ(0u, flags); + + // The desired display state has been set to the new value. + EXPECT_EQ(1u, display.getCurrentDisplayState().flags); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfFlagsChanged) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display has a layer stack set + display.mutableCurrentDisplayState().flags = 0u; + + // The incoming request sets a different layer stack + DisplayState state; + state.what = DisplayState::eFlagsChanged; + state.token = display.token(); + state.flags = 1u; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The desired display state has been set to the new value. + EXPECT_EQ(1u, display.getCurrentDisplayState().flags); +} + TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) { using Case = SimplePrimaryDisplayCase; constexpr ui::Rotation initialOrientation = ui::ROTATION_180; diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index 65024202b8..eea1002236 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -260,6 +260,11 @@ struct DisplayPowerCase { auto display = Display::makeFakeExistingDisplayInjector(test); display.inject(); display.mutableDisplayDevice()->setPowerMode(mode); + if (display.mutableDisplayDevice()->isInternal()) { + test->mFlinger.mutableActiveDisplayToken() = + display.mutableDisplayDevice()->getDisplayToken(); + } + return display; } diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 41fd6e316f..1d21bd43e3 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -32,16 +32,19 @@ namespace android { class TestableScheduler : public Scheduler { public: - TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) + TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs, + ISchedulerCallback& callback) : TestableScheduler(std::make_unique<mock::VsyncController>(), - std::make_unique<mock::VSyncTracker>(), configs, callback) {} + std::make_unique<mock::VSyncTracker>(), refreshRateConfigs, + callback) {} TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, - const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) - : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs, - callback, createLayerHistory(configs), - {.supportKernelTimer = false, .useContentDetection = true}) {} + const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs, + ISchedulerCallback& callback) + : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, + refreshRateConfigs, callback, createLayerHistory(), + {.useContentDetection = true}) {} // Used to inject mock event thread. ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { @@ -64,6 +67,8 @@ public: return mutableLayerHistory()->mLayerInfos.size(); } + auto refreshRateConfigs() { return holdRefreshRateConfigs(); } + size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS { if (!mLayerHistory) return 0; return mutableLayerHistory()->mActiveLayersEnd; @@ -95,9 +100,8 @@ public: mFeatures.cachedModeChangedParams.reset(); } - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId, - DisplayModeId modeId, nsecs_t vsyncPeriod) { - return Scheduler::onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod); + void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { + return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); } ~TestableScheduler() { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index cf67593174..a23361e856 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -82,8 +82,8 @@ public: return std::make_unique<scheduler::FakePhaseOffsets>(); } - std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&, - ISchedulerCallback&) override { + std::unique_ptr<Scheduler> createScheduler( + const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override { return nullptr; } @@ -209,6 +209,7 @@ public: ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) { DisplayModes modes{DisplayMode::Builder(0) .setId(DisplayModeId(0)) + .setPhysicalDisplayId(PhysicalDisplayId(0)) .setVsyncPeriod(16'666'667) .setGroup(0) .build()}; @@ -216,25 +217,24 @@ public: if (hasMultipleModes) { modes.emplace_back(DisplayMode::Builder(1) .setId(DisplayModeId(1)) + .setPhysicalDisplayId(PhysicalDisplayId(0)) .setVsyncPeriod(11'111'111) .setGroup(0) .build()); } const auto currMode = DisplayModeId(0); - mFlinger->mRefreshRateConfigs = - std::make_unique<scheduler::RefreshRateConfigs>(modes, currMode); - const auto currFps = - mFlinger->mRefreshRateConfigs->getRefreshRateFromModeId(currMode).getFps(); - mFlinger->mRefreshRateStats = - std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps, - /*powerMode=*/hal::PowerMode::OFF); + mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode); + const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps(); mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps); mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); + mFlinger->mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps, + /*powerMode=*/hal::PowerMode::OFF); mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), - *mFlinger->mRefreshRateConfigs, *(callback ?: this)); + mRefreshRateConfigs, *(callback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); @@ -435,12 +435,14 @@ public: auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; } auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; } + auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; } auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; } auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; } auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; } + auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; } auto fromHandle(const sp<IBinder>& handle) { return mFlinger->fromHandle(handle); @@ -644,6 +646,7 @@ public: DisplayModePtr activeMode = DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG) .setId(mActiveModeId) + .setPhysicalDisplayId(PhysicalDisplayId(0)) .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH) .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT) .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD) @@ -654,6 +657,7 @@ public: DisplayModes modes{activeMode}; mCreationArgs.supportedModes = modes; + mCreationArgs.refreshRateConfigs = flinger.mRefreshRateConfigs; } sp<IBinder> token() const { return mDisplayToken; } @@ -723,7 +727,7 @@ public: return *this; } - sp<DisplayDevice> inject() { + sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS { const auto displayId = mCreationArgs.compositionDisplay->getDisplayId(); DisplayDeviceState state; @@ -770,6 +774,7 @@ private: surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization); TestableScheduler* mScheduler = nullptr; + std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 7c431a077b..1a50427b93 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -74,6 +74,7 @@ public: EXPECT_CALL(*mVSyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController), std::unique_ptr<mock::VSyncTracker>(mVSyncTracker), std::move(eventThread), std::move(sfEventThread)); diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 2845d0ab14..a749ece835 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -119,7 +119,7 @@ public: FrameTracer::FrameEvent::QUEUE, /*duration*/ 0)); layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache, frameNumber, dequeueTime, FrameTimelineInfo{}, - nullptr /* releaseBufferCallback */); + nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint*/); commitTransaction(layer.get()); bool computeVisisbleRegions; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index 7bf224d557..2a7921f661 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -119,7 +119,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); acquireFence->signalForTest(12); commitTransaction(layer.get()); @@ -147,7 +148,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; @@ -160,7 +162,8 @@ public: mRenderEngine, false); nsecs_t start = systemTime(); layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); nsecs_t end = systemTime(); acquireFence2->signalForTest(12); @@ -200,7 +203,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); acquireFence->signalForTest(12); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); @@ -228,7 +232,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -260,7 +265,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX; @@ -298,7 +304,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; @@ -309,7 +316,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); acquireFence2->signalForTest(12); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -339,7 +347,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX; @@ -353,7 +362,7 @@ public: auto dropStartTime1 = systemTime(); layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */); + nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint */); auto dropEndTime1 = systemTime(); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -367,7 +376,8 @@ public: mRenderEngine, false); auto dropStartTime2 = systemTime(); layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); auto dropEndTime2 = systemTime(); acquireFence3->signalForTest(12); @@ -411,7 +421,8 @@ public: mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, {/*vsyncId*/ 1, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */); + nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2, /*inputEventId*/ 0}, 10); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index cb3bd73920..1ba3c0f56e 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -54,8 +54,7 @@ public: MOCK_METHOD0(resetCommands, void()); MOCK_METHOD0(executeCommands, Error()); MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t()); - MOCK_METHOD5(createVirtualDisplay, - Error(uint32_t, uint32_t, PixelFormat*, std::optional<Display>, Display*)); + MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*)); MOCK_METHOD1(destroyVirtualDisplay, Error(Display)); MOCK_METHOD1(acceptDisplayChanges, Error(Display)); MOCK_METHOD2(createLayer, Error(Display, Layer* outLayer)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 485b4acdce..d25973e1ce 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -33,7 +33,7 @@ public: MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool)); - MOCK_METHOD3(onModeChanged, void(PhysicalDisplayId, DisplayModeId, nsecs_t)); + MOCK_METHOD1(onModeChanged, void(DisplayModePtr)); MOCK_METHOD2(onFrameRateOverridesChanged, void(PhysicalDisplayId, std::vector<FrameRateOverride>)); MOCK_CONST_METHOD1(dump, void(std::string&)); diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h index ddaa5a166e..cae76849bf 100644 --- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -175,6 +175,11 @@ public: void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); } + void expectSize(uint32_t width, uint32_t height) { + EXPECT_EQ(width, mOutBuffer->getWidth()); + EXPECT_EQ(height, mOutBuffer->getHeight()); + } + explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) { if (mOutBuffer) { mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels)); diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index cf774fd9b8..0c5d61b8d5 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -624,8 +624,10 @@ void CreateInfoWrapper::FilterExtension(const char* name) { switch (ext_bit) { case ProcHook::KHR_android_surface: case ProcHook::KHR_surface: + case ProcHook::KHR_surface_protected_capabilities: case ProcHook::EXT_swapchain_colorspace: case ProcHook::KHR_get_surface_capabilities2: + case ProcHook::GOOGLE_surfaceless_query: hook_extensions_.set(ext_bit); // return now as these extensions do not require HAL support return; @@ -701,8 +703,10 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::KHR_external_fence_capabilities: case ProcHook::KHR_get_surface_capabilities2: case ProcHook::KHR_surface: + case ProcHook::KHR_surface_protected_capabilities: case ProcHook::EXT_debug_report: case ProcHook::EXT_swapchain_colorspace: + case ProcHook::GOOGLE_surfaceless_query: case ProcHook::ANDROID_native_buffer: case ProcHook::EXTENSION_CORE_1_0: case ProcHook::EXTENSION_CORE_1_1: @@ -913,6 +917,9 @@ VkResult EnumerateInstanceExtensionProperties( loader_extensions.push_back({ VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION}); + loader_extensions.push_back( + {VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME, + VK_KHR_SURFACE_PROTECTED_CAPABILITIES_SPEC_VERSION}); loader_extensions.push_back({ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_SPEC_VERSION}); @@ -922,6 +929,8 @@ VkResult EnumerateInstanceExtensionProperties( loader_extensions.push_back({ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION}); + loader_extensions.push_back({VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME, + VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION}); static const VkExtensionProperties loader_debug_report_extension = { VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION, diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 5f37a50a03..b436db1de7 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -565,11 +565,13 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_EXT_hdr_metadata") == 0) return ProcHook::EXT_hdr_metadata; if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace; if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing; + if (strcmp(name, "VK_GOOGLE_surfaceless_query") == 0) return ProcHook::GOOGLE_surfaceless_query; if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface; if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2; if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present; if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image; if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface; + if (strcmp(name, "VK_KHR_surface_protected_capabilities") == 0) return ProcHook::KHR_surface_protected_capabilities; if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain; if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer; if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2; diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 047e774004..688630c0a5 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -41,11 +41,13 @@ struct ProcHook { EXT_hdr_metadata, EXT_swapchain_colorspace, GOOGLE_display_timing, + GOOGLE_surfaceless_query, KHR_android_surface, KHR_get_surface_capabilities2, KHR_incremental_present, KHR_shared_presentable_image, KHR_surface, + KHR_surface_protected_capabilities, KHR_swapchain, ANDROID_external_memory_android_hardware_buffer, KHR_bind_memory2, diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 6191063894..0b9ba05be8 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -614,42 +614,65 @@ VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/, VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( - VkPhysicalDevice /*pdev*/, + VkPhysicalDevice pdev, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* capabilities) { ATRACE_CALL(); int err; - ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - int width, height; - err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - int transform_hint; - err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - int max_buffer_count; - err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; + if (surface == VK_NULL_HANDLE) { + const InstanceData& instance_data = GetData(pdev); + ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; + bool surfaceless_enabled = + instance_data.hook_extensions.test(surfaceless); + if (!surfaceless_enabled) { + // It is an error to pass a surface==VK_NULL_HANDLE unless the + // VK_GOOGLE_surfaceless_query extension is enabled + return VK_ERROR_SURFACE_LOST_KHR; + } + // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this + // extension for this function is for + // VkSurfaceProtectedCapabilitiesKHR::supportsProtected. The following + // four values cannot be known without a surface. Default values will + // be supplied anyway, but cannot be relied upon. + width = 1000; + height = 1000; + transform_hint = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + max_buffer_count = 10; + } else { + ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); + + err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + + err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, + &transform_hint); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + + err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, + &max_buffer_count); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } } capabilities->minImageCount = std::min(max_buffer_count, 3); capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); @@ -690,23 +713,43 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, const InstanceData& instance_data = GetData(pdev); bool wide_color_support = false; - Surface& surface = *SurfaceFromHandle(surface_handle); - int err = native_window_get_wide_color_support(surface.window.get(), - &wide_color_support); - if (err) { - return VK_ERROR_SURFACE_LOST_KHR; - } - ALOGV("wide_color_support is: %d", wide_color_support); - wide_color_support = - wide_color_support && + uint64_t consumer_usage = 0; + bool colorspace_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; + } + wide_color_support = wide_color_support && colorspace_ext; AHardwareBuffer_Desc desc = {}; desc.width = 1; desc.height = 1; desc.layers = 1; - desc.usage = surface.consumer_usage | - AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + desc.usage = consumer_usage | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; // We must support R8G8B8A8 @@ -714,6 +757,11 @@ 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 (colorspace_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}); @@ -721,6 +769,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); } + // NOTE: Any new formats that are added must be coordinated across different + // Android users. This includes the ANGLE team (a layered implementation of + // OpenGL-ES). + desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -753,6 +805,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, } } + // NOTE: Any new formats that are added must be coordinated across different + // Android users. This includes the ANGLE team (a layered implementation of + // OpenGL-ES). + VkResult result = VK_SUCCESS; if (formats) { uint32_t transfer_count = all_formats.size(); @@ -797,6 +853,12 @@ VkResult GetPhysicalDeviceSurfaceCapabilities2KHR( .supportedUsageFlags; } break; + case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: { + VkSurfaceProtectedCapabilitiesKHR* protected_caps = + reinterpret_cast<VkSurfaceProtectedCapabilitiesKHR*>(caps); + protected_caps->supportsProtected = VK_TRUE; + } break; + default: // Ignore all other extension structs break; @@ -848,31 +910,51 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, int err; int query_value; - ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); + std::vector<VkPresentModeKHR> present_modes; + if (surface == VK_NULL_HANDLE) { + const InstanceData& instance_data = GetData(pdev); + ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; + bool surfaceless_enabled = + instance_data.hook_extensions.test(surfaceless); + if (!surfaceless_enabled) { + return VK_ERROR_SURFACE_LOST_KHR; + } + // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this + // extension for this function is for + // VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and + // VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR. We technically cannot + // know if VK_PRESENT_MODE_SHARED_MAILBOX_KHR is supported without a + // surface, and that cannot be relied upon. + present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); + present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); + } else { + ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &query_value); - if (err != android::OK || query_value < 0) { - ALOGE( - "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " - "value=%d", - strerror(-err), err, query_value); - return VK_ERROR_SURFACE_LOST_KHR; - } - uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &query_value); + if (err != android::OK || query_value < 0) { + ALOGE( + "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " + "value=%d", + strerror(-err), err, query_value); + return VK_ERROR_SURFACE_LOST_KHR; + } + uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); + + err = + window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); + if (err != android::OK || query_value < 0) { + ALOGE( + "NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d", + strerror(-err), err, query_value); + return VK_ERROR_SURFACE_LOST_KHR; + } + uint32_t max_buffer_count = static_cast<uint32_t>(query_value); - err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); - if (err != android::OK || query_value < 0) { - ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d", - strerror(-err), err, query_value); - return VK_ERROR_SURFACE_LOST_KHR; + if (min_undequeued_buffers + 1 < max_buffer_count) + present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); + present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } - uint32_t max_buffer_count = static_cast<uint32_t>(query_value); - - std::vector<VkPresentModeKHR> present_modes; - if (min_undequeued_buffers + 1 < max_buffer_count) - present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); - present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); VkPhysicalDevicePresentationPropertiesANDROID present_properties; QueryPresentationProperties(pdev, &present_properties); diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py index 6a73023193..af56764a21 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -27,11 +27,13 @@ _INTERCEPTED_EXTENSIONS = [ 'VK_EXT_hdr_metadata', 'VK_EXT_swapchain_colorspace', 'VK_GOOGLE_display_timing', + 'VK_GOOGLE_surfaceless_query', 'VK_KHR_android_surface', 'VK_KHR_get_surface_capabilities2', 'VK_KHR_incremental_present', 'VK_KHR_shared_presentable_image', 'VK_KHR_surface', + 'VK_KHR_surface_protected_capabilities', 'VK_KHR_swapchain', ] |