diff options
187 files changed, 5902 insertions, 3592 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 4dd4f7999d..f0545960a7 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,6 +1,6 @@ [Builtin Hooks] -clang_format = true bpfmt = true +clang_format = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 01c4723f29..34ccb21c1e 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -282,6 +282,21 @@ on late-init chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu23/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu23/trace +# Setup synthetic events + chmod 0666 /sys/kernel/tracing/synthetic_events + chmod 0666 /sys/kernel/debug/tracing/synthetic_events + + # rss_stat_throttled + write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" + write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" + +# Set up histogram triggers + # rss_stat_throttled (bucket size == 512KB) + chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger + chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger + write /sys/kernel/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" + write /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" + # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created # will likely fail with EBUSY as it would be in use by traced_probes. diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 157d259a9b..8d23efced8 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -422,9 +422,131 @@ static bool prepare_app_profile_dir(const std::string& packageName, int32_t appI return true; } +static bool chown_app_dir(const std::string& path, uid_t uid, uid_t previousUid, gid_t cacheGid) { + FTS* fts; + char *argv[] = { (char*) path.c_str(), nullptr }; + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { + return false; + } + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + if (p->fts_info == FTS_D && p->fts_level == 1 + && (strcmp(p->fts_name, "cache") == 0 + || strcmp(p->fts_name, "code_cache") == 0)) { + // Mark cache dirs + p->fts_number = 1; + } else { + // Inherit parent's number + p->fts_number = p->fts_parent->fts_number; + } + + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (p->fts_statp->st_uid == previousUid) { + if (lchown(p->fts_path, uid, p->fts_number ? cacheGid : uid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + } else { + LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected UID " + << p->fts_statp->st_uid << " instead of " << previousUid; + } + break; + } + } + fts_close(fts); + return true; +} + +static void chown_app_profile_dir(const std::string &packageName, int32_t appId, int32_t userId) { + uid_t uid = multiuser_get_uid(userId, appId); + gid_t sharedGid = multiuser_get_shared_gid(userId, appId); + + const std::string profile_dir = + create_primary_current_profile_package_dir_path(userId, packageName); + char *argv[] = { (char*) profile_dir.c_str(), nullptr }; + if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) { + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (lchown(p->fts_path, uid, uid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + break; + } + } + fts_close(fts); + } + + const std::string ref_profile_path = + create_primary_reference_profile_package_dir_path(packageName); + argv[0] = (char *) ref_profile_path.c_str(); + if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) { + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + if (p->fts_info == FTS_D && p->fts_level == 0) { + if (chown(p->fts_path, AID_SYSTEM, sharedGid) != 0) { + PLOG(WARNING) << "Failed to chown " << p->fts_path; + } + continue; + } + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (lchown(p->fts_path, sharedGid, sharedGid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + break; + } + } + fts_close(fts); + } +} + +static binder::Status createAppDataDirs(const std::string& path, + int32_t uid, int32_t* previousUid, int32_t cacheGid, + const std::string& seInfo, mode_t targetMode) { + struct stat st{}; + bool existing = (stat(path.c_str(), &st) == 0); + if (existing) { + 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)) { + return error("Failed to chown " + path); + } + } + } + + 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)) { + return error("Failed to prepare " + path); + } + + // Consider restorecon over contents if label changed + if (restorecon_app_data_lazy(path, seInfo, uid, existing) || + restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || + restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { + return error("Failed to restorecon " + path); + } + + return ok(); +} + binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { + int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, + int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -437,6 +559,14 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str if (_aidl_return != nullptr) *_aidl_return = -1; 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); + int32_t cacheGid = multiuser_get_cache_gid(userId, appId); mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; @@ -447,19 +577,13 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); - bool existing = (access(path.c_str(), F_OK) == 0); - 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)) { - return error("Failed to prepare " + path); + auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode); + if (!status.isOk()) { + return status; } - - // Consider restorecon over contents if label changed - if (restorecon_app_data_lazy(path, seInfo, uid, existing) || - restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || - restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { - return error("Failed to restorecon " + path); + if (previousUid != uid) { + chown_app_profile_dir(packageName, appId, userId); } // Remember inode numbers of cache directories so that we can clear @@ -481,19 +605,10 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); - bool existing = (access(path.c_str(), F_OK) == 0); - - 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)) { - return error("Failed to prepare " + path); - } - // Consider restorecon over contents if label changed - if (restorecon_app_data_lazy(path, seInfo, uid, existing) || - restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || - restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { - return error("Failed to restorecon " + path); + auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode); + if (!status.isOk()) { + return status; } if (!prepare_app_profile_dir(packageName, appId, userId)) { @@ -503,7 +618,6 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str return ok(); } - binder::Status InstalldNativeService::createAppData( const android::os::CreateAppDataArgs& args, android::os::CreateAppDataResult* _aidl_return) { @@ -512,7 +626,7 @@ binder::Status InstalldNativeService::createAppData( int64_t ceDataInode = -1; auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId, - args.seInfo, args.targetSdkVersion, &ceDataInode); + args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode); _aidl_return->ceDataInode = ceDataInode; _aidl_return->exceptionCode = status.exceptionCode(); _aidl_return->exceptionMessage = status.exceptionMessage(); @@ -526,7 +640,7 @@ binder::Status InstalldNativeService::createAppDataBatched( std::lock_guard<std::recursive_mutex> lock(mLock); std::vector<android::os::CreateAppDataResult> results; - for (auto arg : args) { + for (const auto &arg : args) { android::os::CreateAppDataResult result; createAppData(arg, &result); results.push_back(result); @@ -624,14 +738,11 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri } } if (flags & FLAG_STORAGE_DE) { - std::string suffix = ""; - bool only_cache = false; + std::string suffix; if (flags & FLAG_CLEAR_CACHE_ONLY) { suffix = CACHE_DIR_POSTFIX; - only_cache = true; } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { suffix = CODE_CACHE_DIR_POSTFIX; - only_cache = true; } auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix; @@ -1226,7 +1337,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId, - seInfo, targetSdkVersion, nullptr).isOk()) { + /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) { res = error("Failed to create package target"); goto fail; } @@ -1344,7 +1455,7 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s } binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid, - int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) { + int64_t targetFreeBytes, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); std::lock_guard<std::recursive_mutex> lock(mLock); @@ -1447,12 +1558,6 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> break; } - // Only keep clearing when we haven't pushed into reserved area - if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) { - LOG(DEBUG) << "Refusing to clear cached data in reserved space"; - break; - } - // Find the best tracker to work with; this might involve swapping // if the active tracker is no longer the most over quota bool nextBetter = active && !queue.empty() diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index ae257dfcf8..3fdb01ad8b 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -47,7 +47,8 @@ public: binder::Status createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); + int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, + int64_t* _aidl_return); binder::Status createAppData( const android::os::CreateAppDataArgs& args, @@ -144,7 +145,7 @@ public: binder::Status rmPackageDir(const std::string& packageDir); binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes, - int64_t cacheReservedBytes, int32_t flags); + int32_t flags); binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId); binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet); diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl index 96d7faaaa2..d5e8ee5daf 100644 --- a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl +++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl @@ -23,6 +23,7 @@ parcelable CreateAppDataArgs { int userId; int flags; int appId; + int previousAppId; @utf8InCpp String seInfo; int targetSdkVersion; } diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 637a9f2171..9c51ff749d 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -86,8 +86,7 @@ interface IInstalld { void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName); void rmPackageDir(@utf8InCpp String packageDir); - void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, - long cacheReservedBytes, int flags); + void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, int flags); void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId); void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet); diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 863cdfe55b..9a1e17eb20 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -145,7 +145,7 @@ TEST_F(CacheTest, FreeCache_All) { EXPECT_EQ(0, exists("com.example/cache/foo/one")); EXPECT_EQ(0, exists("com.example/cache/foo/two")); - service->freeCache(testUuid, kTbInBytes, 0, + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(0, exists("com.example/normal")); @@ -153,6 +153,33 @@ TEST_F(CacheTest, FreeCache_All) { EXPECT_EQ(-1, exists("com.example/cache/foo/two")); } +TEST_F(CacheTest, FreeCache_NonAggressive) { + LOG(INFO) << "FreeCache_NonAggressive"; + + mkdir("com.example"); + touch("com.example/normal", 1 * kMbInBytes, 60); + mkdir("com.example/cache"); + mkdir("com.example/cache/foo"); + touch("com.example/cache/foo/one", 65 * kMbInBytes, 60); + touch("com.example/cache/foo/two", 2 * kMbInBytes, 120); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(0, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); +} + TEST_F(CacheTest, FreeCache_Age) { LOG(INFO) << "FreeCache_Age"; @@ -162,13 +189,13 @@ TEST_F(CacheTest, FreeCache_Age) { touch("com.example/cache/foo/one", kMbInBytes, 60); touch("com.example/cache/foo/two", kMbInBytes, 120); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/one")); EXPECT_EQ(0, exists("com.example/cache/foo/two")); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/one")); @@ -196,7 +223,7 @@ TEST_F(CacheTest, FreeCache_Tombstone) { EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1")); EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2")); - service->freeCache(testUuid, kTbInBytes, 0, + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); @@ -218,7 +245,7 @@ TEST_F(CacheTest, FreeCache_Group) { setxattr("com.example/cache/foo", "user.cache_group"); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); @@ -263,7 +290,7 @@ TEST_F(CacheTest, FreeCache_GroupTombstone) { setxattr("com.example/cache/tomb", "user.cache_tombstone"); setxattr("com.example/cache/tomb/group", "user.cache_group"); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1")); @@ -284,7 +311,7 @@ TEST_F(CacheTest, FreeCache_GroupTombstone) { EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2")); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, size("com.example/cache/group/file1")); @@ -305,7 +332,7 @@ TEST_F(CacheTest, FreeCache_GroupTombstone) { EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2")); - service->freeCache(testUuid, kTbInBytes, 0, + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, size("com.example/cache/group/file1")); diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index ea26955af5..a937436f9c 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -287,6 +287,7 @@ protected: kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode_); @@ -1257,6 +1258,7 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode_)); @@ -1320,6 +1322,7 @@ class BootProfileTest : public ProfileTest { kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode)); diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index fdbe85b4a8..fe417a362f 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -45,33 +45,6 @@ void writeString16(Parcel& parcel, const char* string) } } -// get the name of the generic interface we hold a reference to -static String16 get_interface_name(sp<IBinder> service) -{ - if (service != nullptr) { - Parcel data, reply; - data.markForBinder(service); - status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply); - if (err == NO_ERROR) { - return reply.readString16(); - } - } - return String16(); -} - -static String8 good_old_string(const String16& src) -{ - String8 name8; - char ch8[2]; - ch8[1] = 0; - for (unsigned j = 0; j < src.size(); j++) { - char16_t ch = src[j]; - if (ch < 128) ch8[0] = (char)ch; - name8.append(ch8); - } - return name8; -} - int main(int argc, char* const argv[]) { bool wantsUsage = false; @@ -132,8 +105,8 @@ int main(int argc, char* const argv[]) String16 name = services[i]; sp<IBinder> service = sm->checkService(name); aout << i - << "\t" << good_old_string(name) - << ": [" << good_old_string(get_interface_name(service)) << "]" + << "\t" << name + << ": [" << (service ? service->getInterfaceDescriptor() : String16()) << "]" << endl; } } else if (strcmp(argv[optind], "call") == 0) { @@ -141,7 +114,7 @@ int main(int argc, char* const argv[]) if (optind+1 < argc) { int serviceArg = optind; sp<IBinder> service = sm->checkService(String16(argv[optind++])); - String16 ifName = get_interface_name(service); + String16 ifName = (service ? service->getInterfaceDescriptor() : String16()); int32_t code = atoi(argv[optind++]); if (service != nullptr && ifName.size() > 0) { Parcel data, reply; diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 5fe4ea1787..931c5e3f2a 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -89,6 +89,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.fingerprint.prebuilt.xml", + src: "android.hardware.fingerprint.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.location.gps.prebuilt.xml", src: "android.hardware.location.gps.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/include/android/input.h b/include/android/input.h index 6d2c1b3015..27587ce483 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -1385,6 +1385,14 @@ int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event); */ void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled); +/** + * Supplies the AInputQueue* object associated with the supplied Java InputQueue + * object. + * + * Available since API level 33. + */ +AInputQueue* AInputQueue_fromJava(jobject inputQueue) __INTRODUCED_IN(33); + #ifdef __cplusplus } #endif diff --git a/include/input/Input.h b/include/input/Input.h index 54c71140eb..ce9cefed8b 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -801,8 +801,11 @@ public: static std::string actionToString(int32_t action); + // MotionEvent will transform various axes in different ways, based on the source. For + // example, the x and y axes will not have any offsets/translations applied if it comes from a + // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods + // are used to apply these transformations for different axes. static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy); - static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&, const PointerCoords&); @@ -837,15 +840,12 @@ public: inline bool getHasFocus() const { return mHasFocus; } - inline bool getInTouchMode() const { return mInTouchMode; } - - void initialize(int32_t id, bool hasFocus, bool inTouchMode); + void initialize(int32_t id, bool hasFocus); void initialize(const FocusEvent& from); protected: bool mHasFocus; - bool mInTouchMode; }; /* diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index d655b28278..edcb615491 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -178,10 +178,9 @@ struct InputMessage { struct Focus { int32_t eventId; - // The following 3 fields take up 4 bytes total + // The following 2 fields take up 4 bytes total bool hasFocus; - bool inTouchMode; - uint8_t empty[2]; + uint8_t empty[3]; inline size_t size() const { return sizeof(Focus); } } focus; @@ -381,7 +380,7 @@ public: * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode); + status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus); /* Publishes a capture event to the input channel. * diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp index b74923cbfc..ac3e5b80fd 100644 --- a/libs/android_runtime_lazy/Android.bp +++ b/libs/android_runtime_lazy/Android.bp @@ -42,12 +42,13 @@ package { cc_library { name: "libandroid_runtime_lazy", vendor_available: true, + recovery_available: true, double_loadable: true, host_supported: true, target: { darwin: { enabled: false, - } + }, }, cflags: [ diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index 03e1f2a022..a2b59a877f 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -195,10 +195,18 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { << ", which is lower than the previous value " << valueToString(lastValue) << "\n"; ALOGE("%s", str.str().c_str()); + + for (int i = 0; i < stateCount; i++) { + states[i].timeInStateSinceUpdate = 0; + } } } else if (timestamp < lastUpdateTimestamp) { ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n", (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp); + + for (int i = 0; i < stateCount; i++) { + states[i].timeInStateSinceUpdate = 0; + } } } } diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp index 848fd10d15..876bf745a8 100644 --- a/libs/battery/MultiStateCounterTest.cpp +++ b/libs/battery/MultiStateCounterTest.cpp @@ -176,7 +176,7 @@ TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) { testCounter.setState(0, 0); testCounter.updateValue(6.0, 2000); - // Time moves back. The negative delta from 2000 to 1000 is ignored + // Time moves back. The delta over the negative interval from 2000 to 1000 is ignored testCounter.updateValue(8.0, 1000); double delta = testCounter.updateValue(11.0, 3000); @@ -189,6 +189,27 @@ TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) { EXPECT_DOUBLE_EQ(3.0, delta); } +TEST_F(MultiStateCounterTest, updateValue_nonmonotonic) { + DoubleMultiStateCounter testCounter(2, 0); + testCounter.updateValue(0, 0); + testCounter.setState(0, 0); + testCounter.updateValue(6.0, 2000); + + // Value goes down. The negative delta from 6.0 to 4.0 is ignored + testCounter.updateValue(4.0, 3000); + + // Value goes up again. The positive delta from 4.0 to 7.0 is accumulated. + double delta = testCounter.updateValue(7.0, 4000); + + // The total accumulated count is: + // 6.0 // For the period 0-2000 + // +(7.0-4.0) // For the period 3000-4000 + EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0)); + + // 7.0-4.0 + EXPECT_DOUBLE_EQ(3.0, delta); +} + TEST_F(MultiStateCounterTest, addValue) { DoubleMultiStateCounter testCounter(1, 0); testCounter.updateValue(0, 0); diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 56cf76f458..8270ae5ce7 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -25,6 +25,7 @@ cc_library_headers { name: "libbinder_headers", export_include_dirs: ["include"], vendor_available: true, + recovery_available: true, host_supported: true, // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, @@ -75,6 +76,7 @@ cc_library { vndk: { enabled: true, }, + recovery_available: true, double_loadable: true, host_supported: true, // TODO(b/153609531): remove when no longer needed. @@ -147,6 +149,11 @@ cc_library { "UtilsHost.cpp", ], }, + recovery: { + exclude_header_libs: [ + "libandroid_runtime_vm_headers", + ], + }, }, aidl: { diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS index 1c8bdea27d..f954e74eba 100644 --- a/libs/binder/OWNERS +++ b/libs/binder/OWNERS @@ -1,5 +1,4 @@ # Bug component: 32456 -arve@google.com ctate@google.com hackbod@google.com maco@google.com diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 181f4051b7..745d9e91d8 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -521,6 +521,25 @@ int Parcel::compareData(const Parcel& other) { return memcmp(data(), other.data(), size); } +status_t Parcel::compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset, + size_t len, int* result) const { + if (len > INT32_MAX || thisOffset > INT32_MAX || otherOffset > INT32_MAX) { + // Don't accept size_t values which may have come from an inadvertent conversion from a + // negative int. + return BAD_VALUE; + } + size_t thisLimit; + if (__builtin_add_overflow(thisOffset, len, &thisLimit) || thisLimit > mDataSize) { + return BAD_VALUE; + } + size_t otherLimit; + if (__builtin_add_overflow(otherOffset, len, &otherLimit) || otherLimit > other.mDataSize) { + return BAD_VALUE; + } + *result = memcmp(data() + thisOffset, other.data() + otherOffset, len); + return NO_ERROR; +} + bool Parcel::allowFds() const { return mAllowFds; @@ -611,6 +630,8 @@ void Parcel::updateWorkSourceRequestHeaderPosition() const { #if defined(__ANDROID_VNDK__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); +#elif defined(__ANDROID_RECOVERY__) +constexpr int32_t kHeader = B_PACK_CHARS('R', 'E', 'C', 'O'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); #endif @@ -2181,12 +2202,14 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, type == BINDER_TYPE_FD)) { // We should never receive other types (eg BINDER_TYPE_FDA) as long as we don't support // them in libbinder. If we do receive them, it probably means a kernel bug; try to - // recover gracefully by clearing out the objects, and releasing the objects we do - // know about. + // recover gracefully by clearing out the objects. android_errorWriteLog(0x534e4554, "135930648"); + android_errorWriteLog(0x534e4554, "203847542"); ALOGE("%s: unsupported type object (%" PRIu32 ") at offset %" PRIu64 "\n", __func__, type, (uint64_t)offset); - releaseObjects(); + + // WARNING: callers of ipcSetDataReference need to make sure they + // don't rely on mObjectsSize in their release_func. mObjectsSize = 0; break; } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index 53c9b78a7a..a5a2bb1017 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -29,13 +29,11 @@ #include <android-base/hex.h> #include <android-base/macros.h> #include <android-base/scopeguard.h> -#include <android_runtime/vm.h> #include <binder/BpBinder.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <binder/Stability.h> -#include <jni.h> #include <utils/String8.h> #include "FdTrigger.h" @@ -48,6 +46,11 @@ extern "C" pid_t gettid(); #endif +#ifndef __ANDROID_RECOVERY__ +#include <android_runtime/vm.h> +#include <jni.h> +#endif + namespace android { using base::unique_fd; @@ -315,6 +318,9 @@ RpcSession::PreJoinSetupResult RpcSession::preJoinSetup( } namespace { +#ifdef __ANDROID_RECOVERY__ +class JavaThreadAttacher {}; +#else // RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If // Android Runtime doesn't exist, no-op. class JavaThreadAttacher { @@ -367,6 +373,7 @@ private: return fn(); } }; +#endif } // namespace void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult) { @@ -374,7 +381,7 @@ void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult if (setupResult.status == OK) { LOG_ALWAYS_FATAL_IF(!connection, "must have connection if setup succeeded"); - JavaThreadAttacher javaThreadAttacher; + [[maybe_unused]] JavaThreadAttacher javaThreadAttacher; while (true) { status_t status = session->state()->getAndExecuteCommand(connection, session, RpcState::CommandType::ANY); diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl index ece79895f7..bffab5e86b 100644 --- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl +++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl @@ -27,4 +27,7 @@ parcelable StagedApexInfo { @utf8InCpp String diskImagePath; long versionCode; @utf8InCpp String versionName; + boolean hasBootClassPathJars; + boolean hasDex2OatBootClassPathJars; + boolean hasSystemServerClassPathJars; } diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index ff90b30380..7d14315b01 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -129,48 +129,50 @@ public: \ #endif -#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\ - const ::android::StaticString16 \ - I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\ - const ::android::String16 I##INTERFACE::descriptor( \ - I##INTERFACE##_descriptor_static_str16); \ - const ::android::String16& \ - I##INTERFACE::getInterfaceDescriptor() const { \ - return I##INTERFACE::descriptor; \ - } \ - ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ - const ::android::sp<::android::IBinder>& obj) \ - { \ - ::android::sp<I##INTERFACE> intr; \ - if (obj != nullptr) { \ - intr = ::android::sp<I##INTERFACE>::cast( \ - obj->queryLocalInterface(I##INTERFACE::descriptor)); \ - if (intr == nullptr) { \ - intr = ::android::sp<Bp##INTERFACE>::make(obj); \ - } \ - } \ - return intr; \ - } \ - std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl; \ - bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\ - { \ - /* Only one user of this interface can use this function */ \ - /* at a time. This is a heuristic to detect if two different */ \ - /* users in the same process use this function. */ \ - assert(!I##INTERFACE::default_impl); \ - if (impl) { \ - I##INTERFACE::default_impl = std::move(impl); \ - return true; \ - } \ - return false; \ - } \ - const std::unique_ptr<I##INTERFACE>& I##INTERFACE::getDefaultImpl() \ - { \ - return I##INTERFACE::default_impl; \ - } \ - I##INTERFACE::I##INTERFACE() { } \ - I##INTERFACE::~I##INTERFACE() { } \ - +// Macro to be used by both IMPLEMENT_META_INTERFACE and IMPLEMENT_META_NESTED_INTERFACE +#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(ITYPE, INAME, BPTYPE) \ + const ::android::String16& ITYPE::getInterfaceDescriptor() const { return ITYPE::descriptor; } \ + ::android::sp<ITYPE> ITYPE::asInterface(const ::android::sp<::android::IBinder>& obj) { \ + ::android::sp<ITYPE> intr; \ + if (obj != nullptr) { \ + intr = ::android::sp<ITYPE>::cast(obj->queryLocalInterface(ITYPE::descriptor)); \ + if (intr == nullptr) { \ + intr = ::android::sp<BPTYPE>::make(obj); \ + } \ + } \ + return intr; \ + } \ + std::unique_ptr<ITYPE> ITYPE::default_impl; \ + bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) { \ + /* Only one user of this interface can use this function */ \ + /* at a time. This is a heuristic to detect if two different */ \ + /* users in the same process use this function. */ \ + assert(!ITYPE::default_impl); \ + if (impl) { \ + ITYPE::default_impl = std::move(impl); \ + return true; \ + } \ + return false; \ + } \ + const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \ + ITYPE::INAME() {} \ + ITYPE::~INAME() {} + +// Macro for an interface type. +#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + const ::android::StaticString16 I##INTERFACE##_descriptor_static_str16( \ + __IINTF_CONCAT(u, NAME)); \ + const ::android::String16 I##INTERFACE::descriptor(I##INTERFACE##_descriptor_static_str16); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(I##INTERFACE, I##INTERFACE, Bp##INTERFACE) + +// Macro for "nested" interface type. +// For example, +// class Parent .. { class INested .. { }; }; +// DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(Parent, Nested, "Parent.INested") +#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(PARENT, INTERFACE, NAME) \ + const ::android::String16 PARENT::I##INTERFACE::descriptor(NAME); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(PARENT::I##INTERFACE, I##INTERFACE, \ + PARENT::Bp##INTERFACE) #define CHECK_INTERFACE(interface, data, reply) \ do { \ diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index d90e803668..9670d7bba6 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -81,6 +81,8 @@ public: size_t start, size_t len); int compareData(const Parcel& other); + status_t compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset, + size_t length, int* result) const; bool allowFds() const; bool pushAllowFds(bool allowFds); @@ -205,6 +207,23 @@ public: status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead"))); status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val); + // Write an IInterface or a vector of IInterface's + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinder(const sp<T>& val) { + return writeStrongBinder(T::asBinder(val)); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinderVector(const std::vector<sp<T>>& val) { + return writeData(val); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinderVector(const std::optional<std::vector<sp<T>>>& val) { + return writeData(val); + } + // Write an Enum vector with underlying type int8_t. // Does not use padding; each byte is contiguous. template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> @@ -419,6 +438,16 @@ public: status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const; status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead"))); status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const; + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t readStrongBinderVector(std::vector<sp<T>>* val) const { + return readData(val); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t readStrongBinderVector(std::optional<std::vector<sp<T>>>* val) const { + return readData(val); + } status_t readByteVector(std::optional<std::vector<int8_t>>* val) const; status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead"))); diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 34f1cbf33a..5baa4d7ad4 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -32,9 +32,11 @@ bool RunRpcServer(AIBinder* service, unsigned int port); bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param), void* param); -// Starts an RPC server on a given port and a given root IBinder object. -// This function sets up the server, calls readyCallback with a given param, and -// then joins before returning. +// Starts an RPC server on a given port and a given root IBinder factory. +// RunRpcServerWithFactory acts like RunRpcServerCallback, but instead of +// assigning single root IBinder object to all connections, factory is called +// whenever a client connects, making it possible to assign unique IBinder +// object to each client. bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context), void* factoryContext, unsigned int port); diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 9c04e581a2..ee46fcb884 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -54,6 +54,7 @@ cc_library { defaults: ["libbinder_ndk_host_user"], host_supported: true, + recovery_available: true, llndk: { symbol_file: "libbinder_ndk.map.txt", @@ -155,6 +156,7 @@ cc_library_headers { name: "libbinder_headers_platform_shared", export_include_dirs: ["include_cpp"], vendor_available: true, + recovery_available: true, host_supported: true, // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 81aa551dac..6949c2c9e5 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -104,6 +104,17 @@ std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz return {}; } +// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char +static std::string SanitizeString(const String16& str) { + std::string sanitized{String8(str)}; + for (auto& c : sanitized) { + if (!isprint(c)) { + c = '?'; + } + } + return sanitized; +} + bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (clazz == nullptr) return false; @@ -118,7 +129,7 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (descriptor != newDescriptor) { if (getBinder()->isBinderAlive()) { LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor - << "' but descriptor is actually '" << descriptor << "'."; + << "' but descriptor is actually '" << SanitizeString(descriptor) << "'."; } else { // b/155793159 LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor @@ -555,6 +566,10 @@ pid_t AIBinder_getCallingPid() { return ::android::IPCThreadState::self()->getCallingPid(); } +bool AIBinder_isHandlingTransaction() { + return ::android::IPCThreadState::self()->getServingStackPointer() != nullptr; +} + void AIBinder_incStrong(AIBinder* binder) { if (binder == nullptr) { return; diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h index 2b18a0a1e0..67623a6556 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h @@ -27,15 +27,67 @@ #pragma once #include <android/binder_auto_utils.h> +#include <android/binder_interface_utils.h> #include <android/binder_internal_logging.h> #include <android/binder_parcel.h> #include <optional> #include <string> +#include <type_traits> #include <vector> namespace ndk { +namespace { +template <typename Test, template <typename...> class Ref> +struct is_specialization : std::false_type {}; + +template <template <typename...> class Ref, typename... Args> +struct is_specialization<Ref<Args...>, Ref> : std::true_type {}; + +template <typename Test, template <typename...> class Ref> +static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value; + +// Get the first template type from a container, the T from MyClass<T, ...>. +template <typename T> +struct first_template_type { + using type = void; +}; + +template <template <typename...> class V, typename T, typename... Args> +struct first_template_type<V<T, Args...>> { + using type = T; +}; + +template <typename T> +using first_template_type_t = typename first_template_type<T>::type; + +// Tells if T represents NDK interface (shared_ptr<ICInterface-derived>) +template <typename T> +static inline constexpr bool is_interface_v = is_specialization_v<T, std::shared_ptr>&& + std::is_base_of_v<::ndk::ICInterface, first_template_type_t<T>>; + +// Tells if T represents NDK parcelable with readFromParcel/writeToParcel methods defined +template <typename T, typename = void> +struct is_parcelable : std::false_type {}; + +template <typename T> +struct is_parcelable< + T, std::void_t<decltype(std::declval<T>().readFromParcel(std::declval<const AParcel*>())), + decltype(std::declval<T>().writeToParcel(std::declval<AParcel*>()))>> + : std::true_type {}; + +template <typename T> +static inline constexpr bool is_parcelable_v = is_parcelable<T>::value; + +// Tells if T represents nullable NDK parcelable (optional<parcelable> or unique_ptr<parcelable>) +template <typename T> +static inline constexpr bool is_nullable_parcelable_v = is_parcelable_v<first_template_type_t<T>> && + (is_specialization_v<T, std::optional> || + is_specialization_v<T, std::unique_ptr>); + +} // namespace + /** * This retrieves and allocates a vector to size 'length' and returns the underlying buffer. */ @@ -429,11 +481,19 @@ static inline binder_status_t AParcel_readVector( */ template <typename P> static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) { - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { - return status; + if constexpr (is_interface_v<P>) { + if (!p) { + return STATUS_UNEXPECTED_NULL; + } + return first_template_type_t<P>::writeToParcel(parcel, p); + } else { + static_assert(is_parcelable_v<P>); + binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null + if (status != STATUS_OK) { + return status; + } + return p.writeToParcel(parcel); } - return p.writeToParcel(parcel); } /** @@ -441,85 +501,81 @@ static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& */ template <typename P> static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - return STATUS_UNEXPECTED_NULL; - } - return p->readFromParcel(parcel); -} - -/** - * Convenience API for writing a nullable parcelable. - */ -template <typename P> -static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, - const std::optional<P>& p) { - if (p == std::nullopt) { - return AParcel_writeInt32(parcel, 0); // null - } - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { + if constexpr (is_interface_v<P>) { + binder_status_t status = first_template_type_t<P>::readFromParcel(parcel, p); + if (status == STATUS_OK) { + if (!*p) { + return STATUS_UNEXPECTED_NULL; + } + } return status; + } else { + static_assert(is_parcelable_v<P>); + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + return STATUS_UNEXPECTED_NULL; + } + return p->readFromParcel(parcel); } - return p->writeToParcel(parcel); } /** * Convenience API for writing a nullable parcelable. */ template <typename P> -static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, - const std::unique_ptr<P>& p) { - if (!p) { - return AParcel_writeInt32(parcel, 0); // null - } - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { - return status; - } - return p->writeToParcel(parcel); -} - -/** - * Convenience API for reading a nullable parcelable. - */ -template <typename P> -static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, - std::optional<P>* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - *p = std::nullopt; - return STATUS_OK; +static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, const P& p) { + if constexpr (is_interface_v<P>) { + return first_template_type_t<P>::writeToParcel(parcel, p); + } else { + static_assert(is_nullable_parcelable_v<P>); + if (!p) { + return AParcel_writeInt32(parcel, 0); // null + } + binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null + if (status != STATUS_OK) { + return status; + } + return p->writeToParcel(parcel); } - *p = std::optional<P>(P{}); - return (*p)->readFromParcel(parcel); } /** * Convenience API for reading a nullable parcelable. */ template <typename P> -static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, - std::unique_ptr<P>* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - p->reset(); - return STATUS_OK; +static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, P* p) { + if constexpr (is_interface_v<P>) { + return first_template_type_t<P>::readFromParcel(parcel, p); + } else if constexpr (is_specialization_v<P, std::optional>) { + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + *p = std::nullopt; + return STATUS_OK; + } + *p = std::optional<first_template_type_t<P>>(first_template_type_t<P>{}); + return (*p)->readFromParcel(parcel); + } else { + static_assert(is_specialization_v<P, std::unique_ptr>); + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + p->reset(); + return STATUS_OK; + } + *p = std::make_unique<first_template_type_t<P>>(); + return (*p)->readFromParcel(parcel); } - *p = std::make_unique<P>(); - return (*p)->readFromParcel(parcel); } /** diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 782328df02..41638976cd 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -393,6 +393,14 @@ uid_t AIBinder_getCallingUid() __INTRODUCED_IN(29); pid_t AIBinder_getCallingPid() __INTRODUCED_IN(29); /** + * Determine whether the current thread is currently executing an incoming transaction. + * + * \return true if the current thread is currently executing an incoming transaction, and false + * otherwise. + */ +bool AIBinder_isHandlingTransaction() __INTRODUCED_IN(33); + +/** * This can only be called if a strong reference to this object already exists in process. * * Available since API level 29. diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 64170af6fc..197c0a1db0 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -119,11 +119,11 @@ LIBBINDER_NDK31 { # introduced=31 AIBinder_setRequestingSid; # apex AParcel_markSensitive; # systemapi llndk AServiceManager_forEachDeclaredInstance; # apex llndk - AServiceManager_forceLazyServicesPersist; # llndk + AServiceManager_forceLazyServicesPersist; # apex llndk AServiceManager_isDeclared; # apex llndk AServiceManager_isUpdatableViaApex; # apex AServiceManager_reRegister; # llndk - AServiceManager_registerLazyService; # llndk + AServiceManager_registerLazyService; # apex llndk AServiceManager_setActiveServicesCallback; # llndk AServiceManager_tryUnregister; # llndk AServiceManager_waitForService; # apex llndk @@ -145,6 +145,7 @@ LIBBINDER_NDK33 { # introduced=33 global: AIBinder_Class_disableInterfaceTokenHeader; AIBinder_DeathRecipient_setOnUnlinked; + AIBinder_isHandlingTransaction; AIBinder_setMinSchedulerPolicy; # llndk AParcel_marshal; AParcel_unmarshal; diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index ecb044e589..4561d6e494 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -20,6 +20,28 @@ rust_library { "libdowncast_rs", ], host_supported: true, + vendor_available: true, + target: { + darwin: { + enabled: false, + } + }, + apex_available: [ + "//apex_available:platform", + "com.android.compos", + "com.android.virt", + ], +} + +rust_library { + name: "libbinder_tokio_rs", + crate_name: "binder_tokio", + srcs: ["binder_tokio/lib.rs"], + rustlibs: [ + "libbinder_rs", + "libtokio", + ], + host_supported: true, target: { darwin: { enabled: false, @@ -43,6 +65,7 @@ rust_library { "libbinder_ndk", ], host_supported: true, + vendor_available: true, target: { darwin: { enabled: false, @@ -84,6 +107,7 @@ rust_bindgen { "libbinder_ndk", ], host_supported: true, + vendor_available: true, // Currently necessary for host builds // TODO(b/31559095): bionic on host should define this diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs new file mode 100644 index 0000000000..64833b6d60 --- /dev/null +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -0,0 +1,103 @@ +/* + * 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. + */ + +//! This crate lets you use the Tokio `spawn_blocking` pool with AIDL in async +//! Rust code. +//! +//! This crate works by defining a type [`Tokio`], which you can use as the +//! generic parameter in the async version of the trait generated by the AIDL +//! compiler. +//! ```text +//! use binder_tokio::Tokio; +//! +//! binder::get_interface::<dyn SomeAsyncInterface<Tokio>>("..."). +//! ``` +//! +//! [`Tokio`]: crate::Tokio + +use binder::public_api::{BinderAsyncPool, BoxFuture, Strong}; +use binder::{FromIBinder, StatusCode}; +use std::future::Future; + +/// Retrieve an existing service for a particular interface, sleeping for a few +/// seconds if it doesn't yet exist. +pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { + let name = name.to_string(); + let res = tokio::task::spawn_blocking(move || { + binder::public_api::get_interface::<T>(&name) + }).await; + + // The `is_panic` branch is not actually reachable in Android as we compile + // with `panic = abort`. + match res { + Ok(Ok(service)) => Ok(service), + Ok(Err(err)) => Err(err), + Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), + Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION), + Err(_) => Err(StatusCode::UNKNOWN_ERROR), + } +} + +/// Retrieve an existing service for a particular interface, or start it if it +/// is configured as a dynamic service and isn't yet started. +pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { + let name = name.to_string(); + let res = tokio::task::spawn_blocking(move || { + binder::public_api::wait_for_interface::<T>(&name) + }).await; + + // The `is_panic` branch is not actually reachable in Android as we compile + // with `panic = abort`. + match res { + Ok(Ok(service)) => Ok(service), + Ok(Err(err)) => Err(err), + Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), + Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION), + Err(_) => Err(StatusCode::UNKNOWN_ERROR), + } +} + +/// Use the Tokio `spawn_blocking` pool with AIDL. +pub enum Tokio {} + +impl BinderAsyncPool for Tokio { + fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>> + where + F1: FnOnce() -> A, + F2: FnOnce(A) -> Fut, + Fut: Future<Output = Result<B, E>>, + F1: Send + 'static, + F2: Send + 'a, + Fut: Send + 'a, + A: Send + 'static, + B: Send + 'a, + E: From<crate::StatusCode>, + { + let handle = tokio::task::spawn_blocking(spawn_me); + Box::pin(async move { + // The `is_panic` branch is not actually reachable in Android as we compile + // with `panic = abort`. + match handle.await { + Ok(res) => after_spawn(res).await, + Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), + Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()), + Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()), + } + }) + } +} + + diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index cc5dd06c9b..d09ac83785 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -17,7 +17,7 @@ //! Trait definitions for binder objects use crate::error::{status_t, Result, StatusCode}; -use crate::parcel::{OwnedParcel, Parcel}; +use crate::parcel::{Parcel, BorrowedParcel}; use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder}; use crate::sys; @@ -129,7 +129,7 @@ pub trait Remotable: Send + Sync { /// Handle and reply to a request to invoke a transaction on this object. /// /// `reply` may be [`None`] if the sender does not expect a reply. - fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>; + fn on_transact(&self, code: TransactionCode, data: &BorrowedParcel<'_>, reply: &mut BorrowedParcel<'_>) -> Result<()>; /// Handle a request to invoke the dump transaction on this /// object. @@ -167,6 +167,7 @@ pub trait IBinderInternal: IBinder { fn ping_binder(&mut self) -> Result<()>; /// Indicate that the service intends to receive caller security contexts. + #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool); /// Dump this object to the given file handle @@ -177,25 +178,25 @@ pub trait IBinderInternal: IBinder { fn get_extension(&mut self) -> Result<Option<SpIBinder>>; /// Create a Parcel that can be used with `submit_transact`. - fn prepare_transact(&self) -> Result<OwnedParcel>; + fn prepare_transact(&self) -> Result<Parcel>; /// Perform a generic operation with the object. /// - /// The provided [`OwnedParcel`] must have been created by a call to + /// The provided [`Parcel`] must have been created by a call to /// `prepare_transact` on the same binder. /// /// # Arguments /// /// * `code` - Transaction code for the operation. - /// * `data` - [`OwnedParcel`] with input data. + /// * `data` - [`Parcel`] with input data. /// * `flags` - Transaction flags, e.g. marking the transaction as /// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)). fn submit_transact( &self, code: TransactionCode, - data: OwnedParcel, + data: Parcel, flags: TransactionFlags, - ) -> Result<OwnedParcel>; + ) -> Result<Parcel>; /// Perform a generic operation with the object. This is a convenience /// method that internally calls `prepare_transact` followed by @@ -206,15 +207,15 @@ pub trait IBinderInternal: IBinder { /// * `flags` - Transaction flags, e.g. marking the transaction as /// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)) /// * `input_callback` A callback for building the `Parcel`. - fn transact<F: FnOnce(&mut Parcel) -> Result<()>>( + fn transact<F: FnOnce(BorrowedParcel<'_>) -> Result<()>>( &self, code: TransactionCode, flags: TransactionFlags, input_callback: F, ) -> Result<Parcel> { let mut parcel = self.prepare_transact()?; - input_callback(&mut parcel.borrowed())?; - self.submit_transact(code, parcel, flags).map(OwnedParcel::into_parcel) + input_callback(parcel.borrowed())?; + self.submit_transact(code, parcel, flags) } } @@ -475,8 +476,8 @@ impl<I: FromIBinder + ?Sized> Eq for Weak<I> {} /// fn on_transact( /// &self, /// code: TransactionCode, -/// data: &Parcel, -/// reply: &mut Parcel, +/// data: &BorrowedParcel, +/// reply: &mut BorrowedParcel, /// ) -> Result<()> { /// // ... /// } @@ -635,6 +636,7 @@ unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> { pub struct BinderFeatures { /// Indicates that the service intends to receive caller security contexts. This must be true /// for `ThreadState::with_calling_sid` to work. + #[cfg(not(android_vndk))] pub set_requesting_sid: bool, // Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility // when new fields are added. #[non_exhaustive] doesn't work because it prevents struct @@ -655,13 +657,13 @@ pub struct BinderFeatures { /// have the following type: /// /// ``` -/// # use binder::{Interface, TransactionCode, Parcel}; +/// # use binder::{Interface, TransactionCode, BorrowedParcel}; /// # trait Placeholder { /// fn on_transact( /// service: &dyn Interface, /// code: TransactionCode, -/// data: &Parcel, -/// reply: &mut Parcel, +/// data: &BorrowedParcel, +/// reply: &mut BorrowedParcel, /// ) -> binder::Result<()>; /// # } /// ``` @@ -676,7 +678,7 @@ pub struct BinderFeatures { /// using the provided function, `on_transact`. /// /// ``` -/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel}; +/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, BorrowedParcel}; /// /// pub trait IServiceManager: Interface { /// // remote methods... @@ -692,8 +694,8 @@ pub struct BinderFeatures { /// fn on_transact( /// service: &dyn IServiceManager, /// code: TransactionCode, -/// data: &Parcel, -/// reply: &mut Parcel, +/// data: &BorrowedParcel, +/// reply: &mut BorrowedParcel, /// ) -> binder::Result<()> { /// // ... /// Ok(()) @@ -713,12 +715,14 @@ macro_rules! declare_binder_interface { $interface:path[$descriptor:expr] { native: $native:ident($on_transact:path), proxy: $proxy:ident, + $(async: $async_interface:ident,)? } } => { $crate::declare_binder_interface! { $interface[$descriptor] { native: $native($on_transact), proxy: $proxy {}, + $(async: $async_interface,)? stability: $crate::Stability::default(), } } @@ -728,6 +732,7 @@ macro_rules! declare_binder_interface { $interface:path[$descriptor:expr] { native: $native:ident($on_transact:path), proxy: $proxy:ident, + $(async: $async_interface:ident,)? stability: $stability:expr, } } => { @@ -735,6 +740,7 @@ macro_rules! declare_binder_interface { $interface[$descriptor] { native: $native($on_transact), proxy: $proxy {}, + $(async: $async_interface,)? stability: $stability, } } @@ -746,6 +752,7 @@ macro_rules! declare_binder_interface { proxy: $proxy:ident { $($fname:ident: $fty:ty = $finit:expr),* }, + $(async: $async_interface:ident,)? } } => { $crate::declare_binder_interface! { @@ -754,6 +761,7 @@ macro_rules! declare_binder_interface { proxy: $proxy { $($fname: $fty = $finit),* }, + $(async: $async_interface,)? stability: $crate::Stability::default(), } } @@ -765,6 +773,7 @@ macro_rules! declare_binder_interface { proxy: $proxy:ident { $($fname:ident: $fty:ty = $finit:expr),* }, + $(async: $async_interface:ident,)? stability: $stability:expr, } } => { @@ -776,6 +785,7 @@ macro_rules! declare_binder_interface { proxy: $proxy { $($fname: $fty = $finit),* }, + $(async: $async_interface,)? stability: $stability, } } @@ -791,6 +801,8 @@ macro_rules! declare_binder_interface { $($fname:ident: $fty:ty = $finit:expr),* }, + $( async: $async_interface:ident, )? + stability: $stability:expr, } } => { @@ -827,6 +839,7 @@ macro_rules! declare_binder_interface { /// Create a new binder service. pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> { let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability); + #[cfg(not(android_vndk))] $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid); $crate::Strong::new(Box::new(binder)) } @@ -837,7 +850,7 @@ macro_rules! declare_binder_interface { $descriptor } - fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> { + fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> { match $on_transact(&*self.0, code, data, reply) { // The C++ backend converts UNEXPECTED_NULL into an exception Err($crate::StatusCode::UNEXPECTED_NULL) => { @@ -912,19 +925,19 @@ macro_rules! declare_binder_interface { where dyn $interface: $crate::Interface { - fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { let binder = $crate::Interface::as_binder(self); parcel.write(&binder) } } impl $crate::parcel::SerializeOption for dyn $interface + '_ { - fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { parcel.write(&this.map($crate::Interface::as_binder)) } } - impl std::fmt::Debug for dyn $interface { + impl std::fmt::Debug for dyn $interface + '_ { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.pad(stringify!($interface)) } @@ -938,6 +951,73 @@ macro_rules! declare_binder_interface { .expect(concat!("Error cloning interface ", stringify!($interface))) } } + + $( + // Async interface trait implementations. + impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> { + fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> { + use $crate::AssociateClass; + + let existing_class = ibinder.get_class(); + if let Some(class) = existing_class { + if class != <$native as $crate::Remotable>::get_class() && + class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor() + { + // The binder object's descriptor string matches what we + // expect. We still need to treat this local or already + // associated object as remote, because we can't cast it + // into a Rust service object without a matching class + // pointer. + return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?))); + } + } + + if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) { + let service: $crate::Result<$crate::Binder<$native>> = + std::convert::TryFrom::try_from(ibinder.clone()); + if let Ok(service) = service { + // We were able to associate with our expected class and + // the service is local. + todo!() + //return Ok($crate::Strong::new(Box::new(service))); + } else { + // Service is remote + return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?))); + } + } + + Err($crate::StatusCode::BAD_TYPE.into()) + } + } + + impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ { + fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { + let binder = $crate::Interface::as_binder(self); + parcel.write(&binder) + } + } + + impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ { + fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { + parcel.write(&this.map($crate::Interface::as_binder)) + } + } + + impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.pad(stringify!($async_interface)) + } + } + + /// Convert a &dyn $async_interface to Strong<dyn $async_interface> + impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> { + type Owned = $crate::Strong<dyn $async_interface<P>>; + fn to_owned(&self) -> Self::Owned { + self.as_binder().into_interface() + .expect(concat!("Error cloning interface ", stringify!($async_interface))) + } + } + )? }; } @@ -963,26 +1043,26 @@ macro_rules! declare_binder_enum { } impl $crate::parcel::Serialize for $enum { - fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { parcel.write(&self.0) } } impl $crate::parcel::SerializeArray for $enum { - fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { let v: Vec<$backing> = slice.iter().map(|x| x.0).collect(); <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel) } } impl $crate::parcel::Deserialize for $enum { - fn deserialize(parcel: &$crate::parcel::Parcel) -> $crate::Result<Self> { + fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> { parcel.read().map(Self) } } impl $crate::parcel::DeserializeArray for $enum { - fn deserialize_array(parcel: &$crate::parcel::Parcel) -> $crate::Result<Option<Vec<Self>>> { + fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> { let v: Option<Vec<$backing>> = <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?; Ok(v.map(|v| v.into_iter().map(Self).collect())) diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs new file mode 100644 index 0000000000..214c0b5f26 --- /dev/null +++ b/libs/binder/rust/src/binder_async.rs @@ -0,0 +1,55 @@ +/* + * 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. + */ + +use std::future::Future; +use std::pin::Pin; + +/// A type alias for a pinned, boxed future that lets you write shorter code without littering it +/// with Pin and Send bounds. +pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; + +/// A thread pool for running binder transactions. +pub trait BinderAsyncPool { + /// This function should conceptually behave like this: + /// + /// ```text + /// let result = spawn_thread(|| spawn_me()).await; + /// return after_spawn(result).await; + /// ``` + /// + /// If the spawning fails for some reason, the method may also skip the `after_spawn` closure + /// and immediately return an error. + /// + /// The only difference between different implementations should be which + /// `spawn_thread` method is used. For Tokio, it would be `tokio::task::spawn_blocking`. + /// + /// This method has the design it has because the only way to define a trait that + /// allows the return type of the spawn to be chosen by the caller is to return a + /// boxed `Future` trait object, and including `after_spawn` in the trait function + /// allows the caller to avoid double-boxing if they want to do anything to the value + /// returned from the spawned thread. + fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>> + where + F1: FnOnce() -> A, + F2: FnOnce(A) -> Fut, + Fut: Future<Output = Result<B, E>>, + F1: Send + 'static, + F2: Send + 'a, + Fut: Send + 'a, + A: Send + 'static, + B: Send + 'a, + E: From<crate::StatusCode>; +} diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 81b620e85f..cce55c0188 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -50,8 +50,8 @@ //! fn on_transact( //! service: &dyn ITest, //! code: TransactionCode, -//! _data: &Parcel, -//! reply: &mut Parcel, +//! _data: &BorrowedParcel, +//! reply: &mut BorrowedParcel, //! ) -> binder::Result<()> { //! match code { //! SpIBinder::FIRST_CALL_TRANSACTION => { @@ -98,6 +98,7 @@ mod proxy; #[macro_use] mod binder; +mod binder_async; mod error; mod native; mod state; @@ -111,9 +112,10 @@ pub use crate::binder::{ Stability, Strong, TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION, }; +pub use crate::binder_async::{BoxFuture, BinderAsyncPool}; pub use error::{status_t, ExceptionCode, Result, Status, StatusCode}; pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder}; -pub use parcel::{OwnedParcel, Parcel}; +pub use parcel::{BorrowedParcel, Parcel}; pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service}; pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder}; pub use state::{ProcessState, ThreadState}; @@ -133,8 +135,9 @@ pub mod public_api { wait_for_interface, }; pub use super::{ - BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder, - Status, StatusCode, Strong, ThreadState, Weak, WpIBinder, + BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder, + Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak, + WpIBinder, }; /// Binder result containing a [`Status`] on error. diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index a91092e2d4..e183ea30fc 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -18,7 +18,7 @@ use crate::binder::{ AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode, }; use crate::error::{status_result, status_t, Result, StatusCode}; -use crate::parcel::{Parcel, Serialize}; +use crate::parcel::{BorrowedParcel, Serialize}; use crate::proxy::SpIBinder; use crate::sys; @@ -161,8 +161,8 @@ impl<T: Remotable> Binder<T> { /// # fn on_transact( /// # service: &dyn IBar, /// # code: TransactionCode, - /// # data: &Parcel, - /// # reply: &mut Parcel, + /// # data: &BorrowedParcel, + /// # reply: &mut BorrowedParcel, /// # ) -> binder::Result<()> { /// # Ok(()) /// # } @@ -212,7 +212,7 @@ impl<T: Remotable> Binder<T> { /// Mark this binder object with local stability, which is vendor if we are /// building for the VNDK and system otherwise. - #[cfg(vendor_ndk)] + #[cfg(any(vendor_ndk, android_vndk))] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so @@ -223,7 +223,7 @@ impl<T: Remotable> Binder<T> { /// Mark this binder object with local stability, which is vendor if we are /// building for the VNDK and system otherwise. - #[cfg(not(vendor_ndk))] + #[cfg(not(any(vendor_ndk, android_vndk)))] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so @@ -277,8 +277,8 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { reply: *mut sys::AParcel, ) -> status_t { let res = { - let mut reply = Parcel::borrowed(reply).unwrap(); - let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap(); + let mut reply = BorrowedParcel::from_raw(reply).unwrap(); + let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap(); let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); binder.on_transact(code, &data, &mut reply) @@ -384,7 +384,7 @@ impl<T: Remotable> Deref for Binder<T> { } impl<B: Remotable> Serialize for Binder<B> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write_binder(Some(&self.as_binder())) } } @@ -503,8 +503,8 @@ impl Remotable for () { fn on_transact( &self, _code: TransactionCode, - _data: &Parcel, - _reply: &mut Parcel, + _data: &BorrowedParcel<'_>, + _reply: &mut BorrowedParcel<'_>, ) -> Result<()> { Ok(()) } diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index 9dba950677..206b90c807 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -21,11 +21,10 @@ use crate::error::{status_result, Result, StatusCode}; use crate::proxy::SpIBinder; use crate::sys; -use std::cell::RefCell; use std::convert::TryInto; use std::marker::PhantomData; use std::mem::ManuallyDrop; -use std::ptr; +use std::ptr::{self, NonNull}; use std::fmt; mod file_descriptor; @@ -46,41 +45,42 @@ pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata}; /// other side of the IPC, and references to live Binder objects that will /// result in the other side receiving a proxy Binder connected with the /// original Binder in the Parcel. -pub enum Parcel { - /// Owned parcel pointer - Owned(*mut sys::AParcel), - /// Borrowed parcel pointer (will not be destroyed on drop) - Borrowed(*mut sys::AParcel), -} - -/// A variant of Parcel that is known to be owned. -pub struct OwnedParcel { - ptr: *mut sys::AParcel, +/// +/// This type represents a parcel that is owned by Rust code. +#[repr(transparent)] +pub struct Parcel { + ptr: NonNull<sys::AParcel>, } /// # Safety /// /// This type guarantees that it owns the AParcel and that all access to -/// the AParcel happens through the OwnedParcel, so it is ok to send across +/// the AParcel happens through the Parcel, so it is ok to send across /// threads. -unsafe impl Send for OwnedParcel {} +unsafe impl Send for Parcel {} -/// A variant of Parcel that is known to be borrowed. +/// Container for a message (data and object references) that can be sent +/// through Binder. +/// +/// This object is a borrowed variant of [`Parcel`]. It is a separate type from +/// `&mut Parcel` because it is not valid to `mem::swap` two parcels. +#[repr(transparent)] pub struct BorrowedParcel<'a> { - inner: Parcel, + ptr: NonNull<sys::AParcel>, _lifetime: PhantomData<&'a mut Parcel>, } -impl OwnedParcel { - /// Create a new empty `OwnedParcel`. - pub fn new() -> OwnedParcel { +impl Parcel { + /// Create a new empty `Parcel`. + pub fn new() -> Parcel { let ptr = unsafe { // Safety: If `AParcel_create` succeeds, it always returns // a valid pointer. If it fails, the process will crash. sys::AParcel_create() }; - assert!(!ptr.is_null()); - Self { ptr } + Self { + ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") + } } /// Create an owned reference to a parcel object from a raw pointer. @@ -94,62 +94,93 @@ impl OwnedParcel { /// /// Additionally, the caller must guarantee that it is valid to take /// ownership of the AParcel object. All future access to the AParcel - /// must happen through this `OwnedParcel`. + /// must happen through this `Parcel`. /// - /// Because `OwnedParcel` implements `Send`, the pointer must never point - /// to any thread-local data, e.g., a variable on the stack, either directly - /// or indirectly. - pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<OwnedParcel> { - ptr.as_mut().map(|ptr| Self { ptr }) + /// Because `Parcel` implements `Send`, the pointer must never point to any + /// thread-local data, e.g., a variable on the stack, either directly or + /// indirectly. + pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<Parcel> { + NonNull::new(ptr).map(|ptr| Self { ptr }) } /// Consume the parcel, transferring ownership to the caller. pub(crate) fn into_raw(self) -> *mut sys::AParcel { - let ptr = self.ptr; + let ptr = self.ptr.as_ptr(); let _ = ManuallyDrop::new(self); ptr } - /// Convert this `OwnedParcel` into an owned `Parcel`. - pub fn into_parcel(self) -> Parcel { - Parcel::Owned(self.into_raw()) - } - /// Get a borrowed view into the contents of this `Parcel`. pub fn borrowed(&mut self) -> BorrowedParcel<'_> { + // Safety: The raw pointer is a valid pointer to an AParcel, and the + // lifetime of the returned `BorrowedParcel` is tied to `self`, so the + // borrow checker will ensure that the `AParcel` can only be accessed + // via the `BorrowParcel` until it goes out of scope. BorrowedParcel { - inner: Parcel::Borrowed(self.ptr), + ptr: self.ptr, _lifetime: PhantomData, } } + + /// Get an immutable borrowed view into the contents of this `Parcel`. + pub fn borrowed_ref(&self) -> &BorrowedParcel<'_> { + // Safety: Parcel and BorrowedParcel are both represented in the same + // way as a NonNull<sys::AParcel> due to their use of repr(transparent), + // so casting references as done here is valid. + unsafe { + &*(self as *const Parcel as *const BorrowedParcel<'_>) + } + } } -impl Default for OwnedParcel { +impl Default for Parcel { fn default() -> Self { Self::new() } } -impl Clone for OwnedParcel { +impl Clone for Parcel { fn clone(&self) -> Self { let mut new_parcel = Self::new(); new_parcel .borrowed() - .append_all_from(&Parcel::Borrowed(self.ptr)) + .append_all_from(self.borrowed_ref()) .expect("Failed to append from Parcel"); new_parcel } } -impl<'a> std::ops::Deref for BorrowedParcel<'a> { - type Target = Parcel; - fn deref(&self) -> &Parcel { - &self.inner +impl<'a> BorrowedParcel<'a> { + /// Create a borrowed reference to a parcel object from a raw pointer. + /// + /// # Safety + /// + /// This constructor is safe if the raw pointer parameter is either null + /// (resulting in `None`), or a valid pointer to an `AParcel` object. + /// + /// Since the raw pointer is not restricted by any lifetime, the lifetime on + /// the returned `BorrowedParcel` object can be chosen arbitrarily by the + /// caller. The caller must ensure it is valid to mutably borrow the AParcel + /// for the duration of the lifetime that the caller chooses. Note that + /// since this is a mutable borrow, it must have exclusive access to the + /// AParcel for the duration of the borrow. + pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<BorrowedParcel<'a>> { + Some(Self { + ptr: NonNull::new(ptr)?, + _lifetime: PhantomData, + }) } -} -impl<'a> std::ops::DerefMut for BorrowedParcel<'a> { - fn deref_mut(&mut self) -> &mut Parcel { - &mut self.inner + + /// Get a sub-reference to this reference to the parcel. + pub fn reborrow(&mut self) -> BorrowedParcel<'_> { + // Safety: The raw pointer is a valid pointer to an AParcel, and the + // lifetime of the returned `BorrowedParcel` is tied to `self`, so the + // borrow checker will ensure that the `AParcel` can only be accessed + // via the `BorrowParcel` until it goes out of scope. + BorrowedParcel { + ptr: self.ptr, + _lifetime: PhantomData, + } } } @@ -159,63 +190,30 @@ impl<'a> std::ops::DerefMut for BorrowedParcel<'a> { /// contain a valid pointer to an `AParcel`. unsafe impl AsNative<sys::AParcel> for Parcel { fn as_native(&self) -> *const sys::AParcel { - match *self { - Self::Owned(x) | Self::Borrowed(x) => x, - } + self.ptr.as_ptr() } fn as_native_mut(&mut self) -> *mut sys::AParcel { - match *self { - Self::Owned(x) | Self::Borrowed(x) => x, - } - } -} - -impl Parcel { - /// Create a new empty `Parcel`. - /// - /// Creates a new owned empty parcel that can be written to - /// using the serialization methods and appended to and - /// from using `append_from` and `append_from_all`. - pub fn new() -> Parcel { - let parcel = unsafe { - // Safety: If `AParcel_create` succeeds, it always returns - // a valid pointer. If it fails, the process will crash. - sys::AParcel_create() - }; - assert!(!parcel.is_null()); - Self::Owned(parcel) - } - - /// Create a borrowed reference to a parcel object from a raw pointer. - /// - /// # Safety - /// - /// This constructor is safe if the raw pointer parameter is either null - /// (resulting in `None`), or a valid pointer to an `AParcel` object. - pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> { - ptr.as_mut().map(|ptr| Self::Borrowed(ptr)) + self.ptr.as_ptr() } } -impl Default for Parcel { - fn default() -> Self { - Self::new() +/// # Safety +/// +/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object +/// will always contain a valid pointer to an `AParcel`. +unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { + fn as_native(&self) -> *const sys::AParcel { + self.ptr.as_ptr() } -} -impl Clone for Parcel { - fn clone(&self) -> Self { - let mut new_parcel = Self::new(); - new_parcel - .append_all_from(self) - .expect("Failed to append from Parcel"); - new_parcel + fn as_native_mut(&mut self) -> *mut sys::AParcel { + self.ptr.as_ptr() } } // Data serialization methods -impl Parcel { +impl<'a> BorrowedParcel<'a> { /// Data written to parcelable is zero'd before being deleted or reallocated. pub fn mark_sensitive(&mut self) { unsafe { @@ -224,12 +222,12 @@ impl Parcel { } } - /// Write a type that implements [`Serialize`] to the `Parcel`. + /// Write a type that implements [`Serialize`] to the parcel. pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { parcelable.serialize(self) } - /// Writes the length of a slice to the `Parcel`. + /// Writes the length of a slice to the parcel. /// /// This is used in AIDL-generated client side code to indicate the /// allocated space for an output array parameter. @@ -242,7 +240,7 @@ impl Parcel { } } - /// Perform a series of writes to the `Parcel`, prepended with the length + /// Perform a series of writes to the parcel, prepended with the length /// (in bytes) of the written data. /// /// The length `0i32` will be written to the parcel first, followed by the @@ -256,7 +254,7 @@ impl Parcel { /// /// ``` /// # use binder::{Binder, Interface, Parcel}; - /// # let mut parcel = Parcel::Owned(std::ptr::null_mut()); + /// # let mut parcel = Parcel::new(); /// parcel.sized_write(|subparcel| { /// subparcel.write(&1u32)?; /// subparcel.write(&2u32)?; @@ -270,14 +268,14 @@ impl Parcel { /// [16i32, 1u32, 2u32, 3u32] /// ``` pub fn sized_write<F>(&mut self, f: F) -> Result<()> - where for<'a> - F: Fn(&'a WritableSubParcel<'a>) -> Result<()> + where + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> { let start = self.get_data_position(); self.write(&0i32)?; { - let subparcel = WritableSubParcel(RefCell::new(self)); - f(&subparcel)?; + let mut subparcel = WritableSubParcel(self.reborrow()); + f(&mut subparcel)?; } let end = self.get_data_position(); unsafe { @@ -294,8 +292,8 @@ impl Parcel { /// Returns the current position in the parcel data. pub fn get_data_position(&self) -> i32 { unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel`, - // and this call is otherwise safe. + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. sys::AParcel_getDataPosition(self.as_native()) } } @@ -303,8 +301,8 @@ impl Parcel { /// Returns the total size of the parcel. pub fn get_data_size(&self) -> i32 { unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel`, - // and this call is otherwise safe. + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. sys::AParcel_getDataSize(self.as_native()) } } @@ -322,11 +320,11 @@ impl Parcel { status_result(sys::AParcel_setDataPosition(self.as_native(), pos)) } - /// Append a subset of another `Parcel`. + /// Append a subset of another parcel. /// /// This appends `size` bytes of data from `other` starting at offset - /// `start` to the current `Parcel`, or returns an error if not possible. - pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> { + /// `start` to the current parcel, or returns an error if not possible. + pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { let status = unsafe { // Safety: `Parcel::appendFrom` from C++ checks that `start` // and `size` are in bounds, and returns an error otherwise. @@ -341,33 +339,125 @@ impl Parcel { status_result(status) } - /// Append the contents of another `Parcel`. - pub fn append_all_from(&mut self, other: &Self) -> Result<()> { - self.append_from(other, 0, other.get_data_size()) + /// Append the contents of another parcel. + pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> { + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. + let size = unsafe { sys::AParcel_getDataSize(other.as_native()) }; + self.append_from(other, 0, size) } } -/// A segment of a writable parcel, used for [`Parcel::sized_write`]. -pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>); +/// A segment of a writable parcel, used for [`BorrowedParcel::sized_write`]. +pub struct WritableSubParcel<'a>(BorrowedParcel<'a>); impl<'a> WritableSubParcel<'a> { /// Write a type that implements [`Serialize`] to the sub-parcel. - pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> { - parcelable.serialize(&mut *self.0.borrow_mut()) + pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { + parcelable.serialize(&mut self.0) } } -// Data deserialization methods impl Parcel { - /// Attempt to read a type that implements [`Deserialize`] from this - /// `Parcel`. + /// Data written to parcelable is zero'd before being deleted or reallocated. + pub fn mark_sensitive(&mut self) { + self.borrowed().mark_sensitive() + } + + /// Write a type that implements [`Serialize`] to the parcel. + pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { + self.borrowed().write(parcelable) + } + + /// Writes the length of a slice to the parcel. + /// + /// This is used in AIDL-generated client side code to indicate the + /// allocated space for an output array parameter. + pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> { + self.borrowed().write_slice_size(slice) + } + + /// Perform a series of writes to the parcel, prepended with the length + /// (in bytes) of the written data. + /// + /// The length `0i32` will be written to the parcel first, followed by the + /// writes performed by the callback. The initial length will then be + /// updated to the length of all data written by the callback, plus the + /// size of the length elemement itself (4 bytes). + /// + /// # Examples + /// + /// After the following call: + /// + /// ``` + /// # use binder::{Binder, Interface, Parcel}; + /// # let mut parcel = Parcel::new(); + /// parcel.sized_write(|subparcel| { + /// subparcel.write(&1u32)?; + /// subparcel.write(&2u32)?; + /// subparcel.write(&3u32) + /// }); + /// ``` + /// + /// `parcel` will contain the following: + /// + /// ```ignore + /// [16i32, 1u32, 2u32, 3u32] + /// ``` + pub fn sized_write<F>(&mut self, f: F) -> Result<()> + where + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> + { + self.borrowed().sized_write(f) + } + + /// Returns the current position in the parcel data. + pub fn get_data_position(&self) -> i32 { + self.borrowed_ref().get_data_position() + } + + /// Returns the total size of the parcel. + pub fn get_data_size(&self) -> i32 { + self.borrowed_ref().get_data_size() + } + + /// Move the current read/write position in the parcel. + /// + /// # Safety + /// + /// This method is safe if `pos` is less than the current size of the parcel + /// data buffer. Otherwise, we are relying on correct bounds checking in the + /// Parcel C++ code on every subsequent read or write to this parcel. If all + /// accesses are bounds checked, this call is still safe, but we can't rely + /// on that. + pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { + self.borrowed_ref().set_data_position(pos) + } + + /// Append a subset of another parcel. + /// + /// This appends `size` bytes of data from `other` starting at offset + /// `start` to the current parcel, or returns an error if not possible. + pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { + self.borrowed().append_from(other, start, size) + } + + /// Append the contents of another parcel. + pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> { + self.borrowed().append_all_from(other) + } +} + +// Data deserialization methods +impl<'a> BorrowedParcel<'a> { + /// Attempt to read a type that implements [`Deserialize`] from this parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { D::deserialize(self) } - /// Attempt to read a type that implements [`Deserialize`] from this - /// `Parcel` onto an existing value. This operation will overwrite the old - /// value partially or completely, depending on how much data is available. + /// Attempt to read a type that implements [`Deserialize`] from this parcel + /// onto an existing value. This operation will overwrite the old value + /// partially or completely, depending on how much data is available. pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> { x.deserialize_from(self) } @@ -400,9 +490,9 @@ impl Parcel { /// }); /// ``` /// - pub fn sized_read<F>(&self, mut f: F) -> Result<()> + pub fn sized_read<F>(&self, f: F) -> Result<()> where - for<'a> F: FnMut(ReadableSubParcel<'a>) -> Result<()> + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> { let start = self.get_data_position(); let parcelable_size: i32 = self.read()?; @@ -417,7 +507,10 @@ impl Parcel { } let subparcel = ReadableSubParcel { - parcel: self, + parcel: BorrowedParcel { + ptr: self.ptr, + _lifetime: PhantomData, + }, end_position: end, }; f(subparcel)?; @@ -431,8 +524,8 @@ impl Parcel { Ok(()) } - /// Read a vector size from the `Parcel` and resize the given output vector - /// to be correctly sized for that amount of data. + /// Read a vector size from the parcel and resize the given output vector to + /// be correctly sized for that amount of data. /// /// This method is used in AIDL-generated server side code for methods that /// take a mutable slice reference parameter. @@ -450,7 +543,7 @@ impl Parcel { Ok(()) } - /// Read a vector size from the `Parcel` and either create a correctly sized + /// Read a vector size from the parcel and either create a correctly sized /// vector for that amount of data or set the output parameter to None if /// the vector should be null. /// @@ -478,7 +571,7 @@ impl Parcel { /// A segment of a readable parcel, used for [`Parcel::sized_read`]. pub struct ReadableSubParcel<'a> { - parcel: &'a Parcel, + parcel: BorrowedParcel<'a>, end_position: i32, } @@ -488,7 +581,7 @@ impl<'a> ReadableSubParcel<'a> { // The caller should have checked this, // but it can't hurt to double-check assert!(self.has_more_data()); - D::deserialize(self.parcel) + D::deserialize(&self.parcel) } /// Check if the sub-parcel has more data to read @@ -497,11 +590,82 @@ impl<'a> ReadableSubParcel<'a> { } } -// Internal APIs impl Parcel { + /// Attempt to read a type that implements [`Deserialize`] from this parcel. + pub fn read<D: Deserialize>(&self) -> Result<D> { + self.borrowed_ref().read() + } + + /// Attempt to read a type that implements [`Deserialize`] from this parcel + /// onto an existing value. This operation will overwrite the old value + /// partially or completely, depending on how much data is available. + pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> { + self.borrowed_ref().read_onto(x) + } + + /// Safely read a sized parcelable. + /// + /// Read the size of a parcelable, compute the end position + /// of that parcelable, then build a sized readable sub-parcel + /// and call a closure with the sub-parcel as its parameter. + /// The closure can keep reading data from the sub-parcel + /// until it runs out of input data. The closure is responsible + /// for calling [`ReadableSubParcel::has_more_data`] to check for + /// more data before every read, at least until Rust generators + /// are stabilized. + /// After the closure returns, skip to the end of the current + /// parcelable regardless of how much the closure has read. + /// + /// # Examples + /// + /// ```no_run + /// let mut parcelable = Default::default(); + /// parcel.sized_read(|subparcel| { + /// if subparcel.has_more_data() { + /// parcelable.a = subparcel.read()?; + /// } + /// if subparcel.has_more_data() { + /// parcelable.b = subparcel.read()?; + /// } + /// Ok(()) + /// }); + /// ``` + /// + pub fn sized_read<F>(&self, f: F) -> Result<()> + where + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> + { + self.borrowed_ref().sized_read(f) + } + + /// Read a vector size from the parcel and resize the given output vector to + /// be correctly sized for that amount of data. + /// + /// This method is used in AIDL-generated server side code for methods that + /// take a mutable slice reference parameter. + pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> { + self.borrowed_ref().resize_out_vec(out_vec) + } + + /// Read a vector size from the parcel and either create a correctly sized + /// vector for that amount of data or set the output parameter to None if + /// the vector should be null. + /// + /// This method is used in AIDL-generated server side code for methods that + /// take a mutable slice reference parameter. + pub fn resize_nullable_out_vec<D: Default + Deserialize>( + &self, + out_vec: &mut Option<Vec<D>>, + ) -> Result<()> { + self.borrowed_ref().resize_nullable_out_vec(out_vec) + } +} + +// Internal APIs +impl<'a> BorrowedParcel<'a> { pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> { unsafe { - // Safety: `Parcel` always contains a valid pointer to an + // Safety: `BorrowedParcel` always contains a valid pointer to an // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return // null or a valid pointer to an `AIBinder`, both of which are // valid, safe inputs to `AParcel_writeStrongBinder`. @@ -521,7 +685,7 @@ impl Parcel { pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> { let mut binder = ptr::null_mut(); let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an + // Safety: `BorrowedParcel` always contains a valid pointer to an // `AParcel`. We pass a valid, mutable out pointer to the `binder` // parameter. After this call, `binder` will be either null or a // valid pointer to an `AIBinder` owned by the caller. @@ -541,25 +705,11 @@ impl Parcel { impl Drop for Parcel { fn drop(&mut self) { // Run the C++ Parcel complete object destructor - if let Self::Owned(ptr) = *self { - unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If we own the parcel, we can safely delete it - // here. - sys::AParcel_delete(ptr) - } - } - } -} - -impl Drop for OwnedParcel { - fn drop(&mut self) { - // Run the C++ Parcel complete object destructor unsafe { - // Safety: `OwnedParcel` always contains a valid pointer to an + // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. Since we own the parcel, we can safely delete it // here. - sys::AParcel_delete(self.ptr) + sys::AParcel_delete(self.ptr.as_ptr()) } } } @@ -571,9 +721,9 @@ impl fmt::Debug for Parcel { } } -impl fmt::Debug for OwnedParcel { +impl<'a> fmt::Debug for BorrowedParcel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OwnedParcel") + f.debug_struct("BorrowedParcel") .finish() } } @@ -595,7 +745,7 @@ fn test_read_write() { assert_eq!(parcel.read::<Option<String>>(), Ok(None)); assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL)); - assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE)); + assert_eq!(parcel.borrowed_ref().read_binder().err(), Some(StatusCode::BAD_TYPE)); parcel.write(&1i32).unwrap(); diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index 8bcc5d0092..b0dea945e0 100644 --- a/libs/binder/rust/src/parcel/file_descriptor.rs +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -15,7 +15,7 @@ */ use super::{ - Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray, + Deserialize, DeserializeArray, DeserializeOption, BorrowedParcel, Serialize, SerializeArray, SerializeOption, }; use crate::binder::AsNative; @@ -61,7 +61,7 @@ impl IntoRawFd for ParcelFileDescriptor { } impl Serialize for ParcelFileDescriptor { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { let fd = self.0.as_raw_fd(); let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -78,7 +78,7 @@ impl Serialize for ParcelFileDescriptor { impl SerializeArray for ParcelFileDescriptor {} impl SerializeOption for ParcelFileDescriptor { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { if let Some(f) = this { f.serialize(parcel) } else { @@ -95,7 +95,7 @@ impl SerializeOption for ParcelFileDescriptor { } impl DeserializeOption for ParcelFileDescriptor { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let mut fd = -1i32; unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -125,7 +125,7 @@ impl DeserializeOption for ParcelFileDescriptor { } impl Deserialize for ParcelFileDescriptor { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel) .transpose() .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index ec00e1dc86..9007cbafc2 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -16,14 +16,14 @@ use crate::binder::{AsNative, FromIBinder, Stability, Strong}; use crate::error::{status_result, status_t, Result, Status, StatusCode}; -use crate::parcel::Parcel; +use crate::parcel::BorrowedParcel; use crate::proxy::SpIBinder; use crate::sys; use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; use std::os::raw::{c_char, c_ulong}; -use std::mem::{self, MaybeUninit}; +use std::mem::{self, MaybeUninit, ManuallyDrop}; use std::ptr; use std::slice; @@ -39,7 +39,7 @@ pub trait Parcelable { /// `Serialize::serialize` and its variants are generally /// preferred over this function, since the former also /// prepend a header. - fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>; + fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>; /// Internal deserialization function for parcelables. /// @@ -47,26 +47,26 @@ pub trait Parcelable { /// `Deserialize::deserialize` and its variants are generally /// preferred over this function, since the former also /// parse the additional header. - fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>; + fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>; } /// A struct whose instances can be written to a [`Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Serialize { /// Serialize this instance into the given [`Parcel`]. - fn serialize(&self, parcel: &mut Parcel) -> Result<()>; + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>; } /// A struct whose instances can be restored from a [`Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Deserialize: Sized { /// Deserialize an instance from the given [`Parcel`]. - fn deserialize(parcel: &Parcel) -> Result<Self>; + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>; /// Deserialize an instance from the given [`Parcel`] onto the /// current object. This operation will overwrite the old value /// partially or completely, depending on how much data is available. - fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> { + fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { *self = Self::deserialize(parcel)?; Ok(()) } @@ -80,8 +80,8 @@ pub trait Deserialize: Sized { // We want the default implementation for most types, but an override for // a few special ones like `readByteArray` for `u8`. pub trait SerializeArray: Serialize + Sized { - /// Serialize an array of this type into the given [`Parcel`]. - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + /// Serialize an array of this type into the given parcel. + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let res = unsafe { // Safety: Safe FFI, slice will always be a safe pointer to pass. sys::AParcel_writeParcelableArray( @@ -111,7 +111,7 @@ unsafe extern "C" fn serialize_element<T: Serialize>( let slice: &[T] = slice::from_raw_parts(array.cast(), index+1); - let mut parcel = match Parcel::borrowed(parcel) { + let mut parcel = match BorrowedParcel::from_raw(parcel) { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -126,8 +126,8 @@ unsafe extern "C" fn serialize_element<T: Serialize>( /// Defaults to calling Deserialize::deserialize() manually for every element, /// but can be overridden for custom implementations like `readByteArray`. pub trait DeserializeArray: Deserialize { - /// Deserialize an array of type from the given [`Parcel`]. - fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> { + /// Deserialize an array of type from the given parcel. + fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> { let mut vec: Option<Vec<MaybeUninit<Self>>> = None; let res = unsafe { // Safety: Safe FFI, vec is the correct opaque type expected by @@ -173,7 +173,7 @@ unsafe extern "C" fn deserialize_element<T: Deserialize>( None => return StatusCode::BAD_INDEX as status_t, }; - let parcel = match Parcel::borrowed(parcel as *mut _) { + let parcel = match BorrowedParcel::from_raw(parcel as *mut _) { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -205,8 +205,8 @@ pub const NULL_PARCELABLE_FLAG: i32 = 0; // We also use it to provide a default implementation for AIDL-generated // parcelables. pub trait SerializeOption: Serialize { - /// Serialize an Option of this type into the given [`Parcel`]. - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + /// Serialize an Option of this type into the given parcel. + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { if let Some(inner) = this { parcel.write(&NON_NULL_PARCELABLE_FLAG)?; parcel.write(inner) @@ -218,8 +218,8 @@ pub trait SerializeOption: Serialize { /// Helper trait for types that can be nullable when deserialized. pub trait DeserializeOption: Deserialize { - /// Deserialize an Option of this type from the given [`Parcel`]. - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + /// Deserialize an Option of this type from the given parcel. + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let null: i32 = parcel.read()?; if null == NULL_PARCELABLE_FLAG { Ok(None) @@ -228,10 +228,10 @@ pub trait DeserializeOption: Deserialize { } } - /// Deserialize an Option of this type from the given [`Parcel`] onto the + /// Deserialize an Option of this type from the given parcel onto the /// current object. This operation will overwrite the current value /// partially or completely, depending on how much data is available. - fn deserialize_option_from(this: &mut Option<Self>, parcel: &Parcel) -> Result<()> { + fn deserialize_option_from(this: &mut Option<Self>, parcel: &BorrowedParcel<'_>) -> Result<()> { *this = Self::deserialize_option(parcel)?; Ok(()) } @@ -297,10 +297,23 @@ macro_rules! parcelable_primitives { }; } +/// Safety: All elements in the vector must be properly initialized. +unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> { + // We can convert from Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T> + // has the same alignment and size as T, so the pointer to the vector + // allocation will be compatible. + let mut vec = ManuallyDrop::new(vec); + Vec::from_raw_parts( + vec.as_mut_ptr().cast(), + vec.len(), + vec.capacity(), + ) +} + macro_rules! impl_parcelable { {Serialize, $ty:ty, $write_fn:path} => { impl Serialize for $ty { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`, and any `$ty` literal value is safe to pass to @@ -313,7 +326,7 @@ macro_rules! impl_parcelable { {Deserialize, $ty:ty, $read_fn:path} => { impl Deserialize for $ty { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut val = Self::default(); unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -329,7 +342,7 @@ macro_rules! impl_parcelable { {SerializeArray, $ty:ty, $write_array_fn:path} => { impl SerializeArray for $ty { - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` @@ -353,7 +366,7 @@ macro_rules! impl_parcelable { {DeserializeArray, $ty:ty, $read_array_fn:path} => { impl DeserializeArray for $ty { - fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> { + fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> { let mut vec: Option<Vec<MaybeUninit<Self>>> = None; let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -371,11 +384,8 @@ macro_rules! impl_parcelable { // Safety: We are assuming that the NDK correctly // initialized every element of the vector by now, so we // know that all the MaybeUninits are now properly - // initialized. We can transmute from Vec<MaybeUninit<T>> to - // Vec<T> because MaybeUninit<T> has the same alignment and - // size as T, so the pointer to the vector allocation will - // be compatible. - mem::transmute(vec) + // initialized. + vec.map(|vec| vec_assume_init(vec)) }; Ok(vec) } @@ -443,19 +453,19 @@ impl SerializeArray for bool {} impl DeserializeArray for bool {} impl Serialize for u8 { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { (*self as i8).serialize(parcel) } } impl Deserialize for u8 { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { i8::deserialize(parcel).map(|v| v as u8) } } impl SerializeArray for u8 { - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a @@ -474,19 +484,19 @@ impl SerializeArray for u8 { } impl Serialize for i16 { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { (*self as u16).serialize(parcel) } } impl Deserialize for i16 { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { u16::deserialize(parcel).map(|v| v as i16) } } impl SerializeArray for i16 { - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a @@ -505,7 +515,7 @@ impl SerializeArray for i16 { } impl SerializeOption for str { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { match this { None => unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -541,7 +551,7 @@ impl SerializeOption for str { } impl Serialize for str { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Some(self).serialize(parcel) } } @@ -549,7 +559,7 @@ impl Serialize for str { impl SerializeArray for &str {} impl Serialize for String { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Some(self.as_str()).serialize(parcel) } } @@ -557,13 +567,13 @@ impl Serialize for String { impl SerializeArray for String {} impl SerializeOption for String { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(String::as_str), parcel) } } impl Deserialize for Option<String> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut vec: Option<Vec<u8>> = None; let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an `AParcel`. @@ -591,7 +601,7 @@ impl Deserialize for Option<String> { impl DeserializeArray for Option<String> {} impl Deserialize for String { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel) .transpose() .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) @@ -601,19 +611,19 @@ impl Deserialize for String { impl DeserializeArray for String {} impl<T: SerializeArray> Serialize for [T] { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeArray::serialize_array(self, parcel) } } impl<T: SerializeArray> Serialize for Vec<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeArray::serialize_array(&self[..], parcel) } } impl<T: SerializeArray> SerializeOption for [T] { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { if let Some(v) = this { SerializeArray::serialize_array(v, parcel) } else { @@ -623,13 +633,13 @@ impl<T: SerializeArray> SerializeOption for [T] { } impl<T: SerializeArray> SerializeOption for Vec<T> { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(Vec::as_slice), parcel) } } impl<T: DeserializeArray> Deserialize for Vec<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { DeserializeArray::deserialize_array(parcel) .transpose() .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) @@ -637,25 +647,25 @@ impl<T: DeserializeArray> Deserialize for Vec<T> { } impl<T: DeserializeArray> DeserializeOption for Vec<T> { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { DeserializeArray::deserialize_array(parcel) } } impl Serialize for Stability { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { i32::from(*self).serialize(parcel) } } impl Deserialize for Stability { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { i32::deserialize(parcel).and_then(Stability::try_from) } } impl Serialize for Status { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { unsafe { // Safety: `Parcel` always contains a valid pointer to an `AParcel` // and `Status` always contains a valid pointer to an `AStatus`, so @@ -670,7 +680,7 @@ impl Serialize for Status { } impl Deserialize for Status { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut status_ptr = ptr::null_mut(); let ret_status = unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -691,56 +701,60 @@ impl Deserialize for Status { } impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Serialize::serialize(&**self, parcel) } } impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(|b| &**b), parcel) } } +impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {} + impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let ibinder: SpIBinder = parcel.read()?; FromIBinder::try_from(ibinder) } } impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let ibinder: Option<SpIBinder> = parcel.read()?; ibinder.map(FromIBinder::try_from).transpose() } } +impl<T: FromIBinder + ?Sized> DeserializeArray for Strong<T> {} + // We need these to support Option<&T> for all T impl<T: Serialize + ?Sized> Serialize for &T { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Serialize::serialize(*self, parcel) } } impl<T: SerializeOption + ?Sized> SerializeOption for &T { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.copied(), parcel) } } impl<T: SerializeOption> Serialize for Option<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(self.as_ref(), parcel) } } impl<T: DeserializeOption> Deserialize for Option<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { DeserializeOption::deserialize_option(parcel) } - fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> { + fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { DeserializeOption::deserialize_option_from(self, parcel) } } @@ -758,7 +772,7 @@ macro_rules! impl_serialize_for_parcelable { impl $crate::parcel::Serialize for $parcelable { fn serialize( &self, - parcel: &mut $crate::parcel::Parcel, + parcel: &mut $crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { <Self as $crate::parcel::SerializeOption>::serialize_option( Some(self), @@ -772,7 +786,7 @@ macro_rules! impl_serialize_for_parcelable { impl $crate::parcel::SerializeOption for $parcelable { fn serialize_option( this: Option<&Self>, - parcel: &mut $crate::parcel::Parcel, + parcel: &mut $crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { if let Some(this) = this { use $crate::parcel::Parcelable; @@ -792,13 +806,12 @@ macro_rules! impl_serialize_for_parcelable { /// `Deserialize`, `DeserializeArray` and `DeserializeOption` for /// structured parcelables. The target type must implement the /// `Parcelable` trait. -/// ``` #[macro_export] macro_rules! impl_deserialize_for_parcelable { ($parcelable:ident) => { impl $crate::parcel::Deserialize for $parcelable { fn deserialize( - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<Self> { $crate::parcel::DeserializeOption::deserialize_option(parcel) .transpose() @@ -806,7 +819,7 @@ macro_rules! impl_deserialize_for_parcelable { } fn deserialize_from( &mut self, - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; if status == $crate::parcel::NULL_PARCELABLE_FLAG { @@ -822,7 +835,7 @@ macro_rules! impl_deserialize_for_parcelable { impl $crate::parcel::DeserializeOption for $parcelable { fn deserialize_option( - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<Option<Self>> { let mut result = None; Self::deserialize_option_from(&mut result, parcel)?; @@ -830,7 +843,7 @@ macro_rules! impl_deserialize_for_parcelable { } fn deserialize_option_from( this: &mut Option<Self>, - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; if status == $crate::parcel::NULL_PARCELABLE_FLAG { @@ -847,326 +860,332 @@ macro_rules! impl_deserialize_for_parcelable { } impl<T: Serialize> Serialize for Box<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Serialize::serialize(&**self, parcel) } } impl<T: Deserialize> Deserialize for Box<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel).map(Box::new) } } impl<T: SerializeOption> SerializeOption for Box<T> { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(|inner| &**inner), parcel) } } impl<T: DeserializeOption> DeserializeOption for Box<T> { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { DeserializeOption::deserialize_option(parcel).map(|t| t.map(Box::new)) } } -#[test] -fn test_custom_parcelable() { - struct Custom(u32, bool, String, Vec<String>); - - impl Serialize for Custom { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { - self.0.serialize(parcel)?; - self.1.serialize(parcel)?; - self.2.serialize(parcel)?; - self.3.serialize(parcel) +#[cfg(test)] +mod tests { + use crate::Parcel; + use super::*; + + #[test] + fn test_custom_parcelable() { + struct Custom(u32, bool, String, Vec<String>); + + impl Serialize for Custom { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { + self.0.serialize(parcel)?; + self.1.serialize(parcel)?; + self.2.serialize(parcel)?; + self.3.serialize(parcel) + } } - } - impl Deserialize for Custom { - fn deserialize(parcel: &Parcel) -> Result<Self> { - Ok(Custom( - parcel.read()?, - parcel.read()?, - parcel.read()?, - parcel.read::<Option<Vec<String>>>()?.unwrap(), - )) + impl Deserialize for Custom { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { + Ok(Custom( + parcel.read()?, + parcel.read()?, + parcel.read()?, + parcel.read::<Option<Vec<String>>>()?.unwrap(), + )) + } } - } - let string8 = "Custom Parcelable".to_string(); + let string8 = "Custom Parcelable".to_string(); - let s1 = "str1".to_string(); - let s2 = "str2".to_string(); - let s3 = "str3".to_string(); + let s1 = "str1".to_string(); + let s2 = "str2".to_string(); + let s3 = "str3".to_string(); - let strs = vec![s1, s2, s3]; + let strs = vec![s1, s2, s3]; - let custom = Custom(123_456_789, true, string8, strs); + let custom = Custom(123_456_789, true, string8, strs); - let mut parcel = Parcel::new(); - let start = parcel.get_data_position(); + let mut parcel = Parcel::new(); + let start = parcel.get_data_position(); - assert!(custom.serialize(&mut parcel).is_ok()); + assert!(custom.serialize(&mut parcel.borrowed()).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let custom2 = Custom::deserialize(&parcel).unwrap(); + let custom2 = Custom::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(custom2.0, 123_456_789); - assert!(custom2.1); - assert_eq!(custom2.2, custom.2); - assert_eq!(custom2.3, custom.3); -} + assert_eq!(custom2.0, 123_456_789); + assert!(custom2.1); + assert_eq!(custom2.2, custom.2); + assert_eq!(custom2.3, custom.3); + } -#[test] -#[allow(clippy::excessive_precision)] -fn test_slice_parcelables() { - let bools = [true, false, false, true]; + #[test] + #[allow(clippy::excessive_precision)] + fn test_slice_parcelables() { + let bools = [true, false, false, true]; - let mut parcel = Parcel::new(); - let start = parcel.get_data_position(); + let mut parcel = Parcel::new(); + let start = parcel.get_data_position(); - assert!(bools.serialize(&mut parcel).is_ok()); + assert!(bools.serialize(&mut parcel.borrowed()).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); - assert_eq!(parcel.read::<u32>().unwrap(), 1); - assert_eq!(parcel.read::<u32>().unwrap(), 0); - assert_eq!(parcel.read::<u32>().unwrap(), 0); - assert_eq!(parcel.read::<u32>().unwrap(), 1); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); + assert_eq!(parcel.read::<u32>().unwrap(), 1); + assert_eq!(parcel.read::<u32>().unwrap(), 0); + assert_eq!(parcel.read::<u32>().unwrap(), 0); + assert_eq!(parcel.read::<u32>().unwrap(), 1); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<bool>::deserialize(&parcel).unwrap(); + let vec = Vec::<bool>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [true, false, false, true]); + assert_eq!(vec, [true, false, false, true]); - let u8s = [101u8, 255, 42, 117]; + let u8s = [101u8, 255, 42, 117]; - let mut parcel = Parcel::new(); - let start = parcel.get_data_position(); + let mut parcel = Parcel::new(); + let start = parcel.get_data_position(); - assert!(parcel.write(&u8s[..]).is_ok()); + assert!(parcel.write(&u8s[..]).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u8>::deserialize(&parcel).unwrap(); - assert_eq!(vec, [101, 255, 42, 117]); + let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap(); + assert_eq!(vec, [101, 255, 42, 117]); - let i8s = [-128i8, 127, 42, -117]; + let i8s = [-128i8, 127, 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert!(parcel.write(&i8s[..]).is_ok()); + assert!(parcel.write(&i8s[..]).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u8>::deserialize(&parcel).unwrap(); - assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]); + let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap(); + assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]); - let u16s = [u16::max_value(), 12_345, 42, 117]; + let u16s = [u16::max_value(), 12_345, 42, 117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(u16s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u16s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u16>::deserialize(&parcel).unwrap(); + let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]); - let i16s = [i16::max_value(), i16::min_value(), 42, -117]; + let i16s = [i16::max_value(), i16::min_value(), 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(i16s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i16s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value() - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<i16>::deserialize(&parcel).unwrap(); + let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]); + assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]); - let u32s = [u32::max_value(), 12_345, 42, 117]; + let u32s = [u32::max_value(), 12_345, 42, 117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(u32s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u32s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u32>::deserialize(&parcel).unwrap(); + let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]); - let i32s = [i32::max_value(), i32::min_value(), 42, -117]; + let i32s = [i32::max_value(), i32::min_value(), 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(i32s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i32s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value() - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<i32>::deserialize(&parcel).unwrap(); + let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]); + assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]); - let u64s = [u64::max_value(), 12_345, 42, 117]; + let u64s = [u64::max_value(), 12_345, 42, 117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(u64s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u64s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u64>::deserialize(&parcel).unwrap(); + let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]); - let i64s = [i64::max_value(), i64::min_value(), 42, -117]; + let i64s = [i64::max_value(), i64::min_value(), 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(i64s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i64s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<i64>::deserialize(&parcel).unwrap(); + let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); + assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); - let f32s = [ - std::f32::NAN, - std::f32::INFINITY, - 1.23456789, - std::f32::EPSILON, - ]; + let f32s = [ + std::f32::NAN, + std::f32::INFINITY, + 1.23456789, + std::f32::EPSILON, + ]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(f32s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(f32s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<f32>::deserialize(&parcel).unwrap(); + let vec = Vec::<f32>::deserialize(parcel.borrowed_ref()).unwrap(); - // NAN != NAN so we can't use it in the assert_eq: - assert!(vec[0].is_nan()); - assert_eq!(vec[1..], f32s[1..]); + // NAN != NAN so we can't use it in the assert_eq: + assert!(vec[0].is_nan()); + assert_eq!(vec[1..], f32s[1..]); - let f64s = [ - std::f64::NAN, - std::f64::INFINITY, - 1.234567890123456789, - std::f64::EPSILON, - ]; + let f64s = [ + std::f64::NAN, + std::f64::INFINITY, + 1.234567890123456789, + std::f64::EPSILON, + ]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(f64s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(f64s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<f64>::deserialize(&parcel).unwrap(); + let vec = Vec::<f64>::deserialize(parcel.borrowed_ref()).unwrap(); - // NAN != NAN so we can't use it in the assert_eq: - assert!(vec[0].is_nan()); - assert_eq!(vec[1..], f64s[1..]); + // NAN != NAN so we can't use it in the assert_eq: + assert!(vec[0].is_nan()); + assert_eq!(vec[1..], f64s[1..]); - let s1 = "Hello, Binder!"; - let s2 = "This is a utf8 string."; - let s3 = "Some more text here."; - let s4 = "Embedded nulls \0 \0"; + let s1 = "Hello, Binder!"; + let s2 = "This is a utf8 string."; + let s3 = "Some more text here."; + let s4 = "Embedded nulls \0 \0"; - let strs = [s1, s2, s3, s4]; + let strs = [s1, s2, s3, s4]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(strs.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(strs.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<String>::deserialize(&parcel).unwrap(); + let vec = Vec::<String>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, strs); + assert_eq!(vec, strs); + } } diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs index bccfd2d25e..b4282b2ec8 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -16,7 +16,7 @@ use crate::binder::Stability; use crate::error::{Result, StatusCode}; -use crate::parcel::{OwnedParcel, Parcel, Parcelable}; +use crate::parcel::{Parcel, BorrowedParcel, Parcelable}; use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable}; use downcast_rs::{impl_downcast, DowncastSync}; @@ -50,7 +50,7 @@ enum ParcelableHolderData { parcelable: Arc<dyn AnyParcelable>, name: String, }, - Parcel(OwnedParcel), + Parcel(Parcel), } impl Default for ParcelableHolderData { @@ -148,7 +148,6 @@ impl ParcelableHolder { } } ParcelableHolderData::Parcel(ref mut parcel) => { - let parcel = parcel.borrowed(); unsafe { // Safety: 0 should always be a valid position. parcel.set_data_position(0)?; @@ -160,7 +159,7 @@ impl ParcelableHolder { } let mut parcelable = T::default(); - parcelable.read_from_parcel(&parcel)?; + parcelable.read_from_parcel(parcel.borrowed_ref())?; let parcelable = Arc::new(parcelable); let result = Arc::clone(&parcelable); @@ -181,7 +180,7 @@ impl_serialize_for_parcelable!(ParcelableHolder); impl_deserialize_for_parcelable!(ParcelableHolder); impl Parcelable for ParcelableHolder { - fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> { + fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write(&self.stability)?; let mut data = self.data.lock().unwrap(); @@ -214,14 +213,13 @@ impl Parcelable for ParcelableHolder { Ok(()) } ParcelableHolderData::Parcel(ref mut p) => { - let p = p.borrowed(); parcel.write(&p.get_data_size())?; - parcel.append_all_from(&p) + parcel.append_all_from(&*p) } } } - fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> { + fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { self.stability = parcel.read()?; let data_size: i32 = parcel.read()?; @@ -242,10 +240,8 @@ impl Parcelable for ParcelableHolder { .checked_add(data_size) .ok_or(StatusCode::BAD_VALUE)?; - let mut new_parcel = OwnedParcel::new(); - new_parcel - .borrowed() - .append_from(parcel, data_start, data_size)?; + let mut new_parcel = Parcel::new(); + new_parcel.append_from(parcel, data_start, data_size)?; *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel); unsafe { diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index a8d0c33034..760d862c53 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -22,8 +22,7 @@ use crate::binder::{ }; use crate::error::{status_result, Result, StatusCode}; use crate::parcel::{ - Deserialize, DeserializeArray, DeserializeOption, OwnedParcel, Parcel, Serialize, SerializeArray, - SerializeOption, + Parcel, BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, }; use crate::sys; @@ -235,7 +234,7 @@ impl Drop for SpIBinder { } impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { - fn prepare_transact(&self) -> Result<OwnedParcel> { + fn prepare_transact(&self) -> Result<Parcel> { let mut input = ptr::null_mut(); let status = unsafe { // Safety: `SpIBinder` guarantees that `self` always contains a @@ -255,16 +254,16 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { // Safety: At this point, `input` is either a valid, owned `AParcel` // pointer, or null. `OwnedParcel::from_raw` safely handles both cases, // taking ownership of the parcel. - OwnedParcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) + Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) } } fn submit_transact( &self, code: TransactionCode, - data: OwnedParcel, + data: Parcel, flags: TransactionFlags, - ) -> Result<OwnedParcel> { + ) -> Result<Parcel> { let mut reply = ptr::null_mut(); let status = unsafe { // Safety: `SpIBinder` guarantees that `self` always contains a @@ -299,7 +298,7 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { // construct a `Parcel` out of it. `AIBinder_transact` passes // ownership of the `reply` parcel to Rust, so we need to // construct an owned variant. - OwnedParcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) + Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) } } @@ -324,6 +323,7 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { status_result(status) } + #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool) { unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) }; } @@ -415,13 +415,13 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { } impl Serialize for SpIBinder { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write_binder(Some(self)) } } impl SerializeOption for SpIBinder { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write_binder(this) } } @@ -429,7 +429,7 @@ impl SerializeOption for SpIBinder { impl SerializeArray for SpIBinder {} impl Deserialize for SpIBinder { - fn deserialize(parcel: &Parcel) -> Result<SpIBinder> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> { parcel .read_binder() .transpose() @@ -438,7 +438,7 @@ impl Deserialize for SpIBinder { } impl DeserializeOption for SpIBinder { - fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<SpIBinder>> { parcel.read_binder() } } diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index 0e05f10dfe..0aef744f98 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -99,6 +99,17 @@ impl ThreadState { } } + /// Determine whether the current thread is currently executing an incoming transaction. + /// + /// \return true if the current thread is currently executing an incoming transaction, and false + /// otherwise. + pub fn is_handling_transaction() -> bool { + unsafe { + // Safety: Safe FFI + sys::AIBinder_isHandlingTransaction() + } + } + /// This function makes the client's security context available to the /// service calling this function. This can be used for access control. /// It does not suffer from the TOCTOU issues of get_calling_pid. diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp index ecc61f4683..2d1175be75 100644 --- a/libs/binder/rust/tests/Android.bp +++ b/libs/binder/rust/tests/Android.bp @@ -13,6 +13,8 @@ rust_test { rustlibs: [ "libbinder_rs", "libselinux_bindgen", + "libbinder_tokio_rs", + "libtokio", ], shared_libs: [ "libselinux", diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index 335e8d848f..1fd2eadc4e 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -17,7 +17,7 @@ //! Rust Binder crate integration tests use binder::declare_binder_interface; -use binder::parcel::Parcel; +use binder::parcel::BorrowedParcel; use binder::{ Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode, FIRST_CALL_TRANSACTION, @@ -154,20 +154,33 @@ pub trait ITest: Interface { fn get_selinux_context(&self) -> binder::Result<String>; } +/// Async trivial testing binder interface +pub trait IATest<P>: Interface { + /// Returns a test string + fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>; + + /// Return the arguments sent via dump + fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>; + + /// Returns the caller's SELinux context + fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>; +} + declare_binder_interface! { ITest["android.os.ITest"] { native: BnTest(on_transact), proxy: BpTest { x: i32 = 100 }, + async: IATest, } } fn on_transact( service: &dyn ITest, code: TransactionCode, - _data: &Parcel, - reply: &mut Parcel, + _data: &BorrowedParcel<'_>, + reply: &mut BorrowedParcel<'_>, ) -> binder::Result<()> { match code.try_into()? { TestTransactionCode::Test => reply.write(&service.test()?), @@ -201,6 +214,32 @@ impl ITest for BpTest { } } +impl<P: binder::BinderAsyncPool> IATest<P> for BpTest { + fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> { + let binder = self.binder.clone(); + P::spawn( + move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } + ) + } + + fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> { + let binder = self.binder.clone(); + P::spawn( + move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } + ) + } + + fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> { + let binder = self.binder.clone(); + P::spawn( + move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } + ) + } +} + impl ITest for Binder<BnTest> { fn test(&self) -> binder::Result<String> { self.0.test() @@ -215,6 +254,23 @@ impl ITest for Binder<BnTest> { } } +impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> { + fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> { + let res = self.0.test(); + Box::pin(async move { res }) + } + + fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> { + let res = self.0.get_dump_args(); + Box::pin(async move { res }) + } + + fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> { + let res = self.0.get_selinux_context(); + Box::pin(async move { res }) + } +} + /// Trivial testing binder interface pub trait ITestSameDescriptor: Interface {} @@ -228,8 +284,8 @@ declare_binder_interface! { fn on_transact_same_descriptor( _service: &dyn ITestSameDescriptor, _code: TransactionCode, - _data: &Parcel, - _reply: &mut Parcel, + _data: &BorrowedParcel<'_>, + _reply: &mut BorrowedParcel<'_>, ) -> binder::Result<()> { Ok(()) } @@ -255,7 +311,9 @@ mod tests { SpIBinder, StatusCode, Strong, }; - use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY}; + use binder_tokio::Tokio; + + use super::{BnTest, ITest, IATest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY}; pub struct ScopedServiceProcess(Child); @@ -303,12 +361,47 @@ mod tests { binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(), Some(StatusCode::NAME_NOT_FOUND) ); + assert_eq!( + binder::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").err(), + Some(StatusCode::NAME_NOT_FOUND) + ); // The service manager service isn't an ITest, so this must fail. assert_eq!( binder::get_interface::<dyn ITest>("manager").err(), Some(StatusCode::BAD_TYPE) ); + assert_eq!( + binder::get_interface::<dyn IATest<Tokio>>("manager").err(), + Some(StatusCode::BAD_TYPE) + ); + } + + #[tokio::test] + async fn check_services_async() { + let mut sm = binder::get_service("manager").expect("Did not get manager binder service"); + assert!(sm.is_binder_alive()); + assert!(sm.ping_binder().is_ok()); + + assert!(binder::get_service("this_service_does_not_exist").is_none()); + assert_eq!( + binder_tokio::get_interface::<dyn ITest>("this_service_does_not_exist").await.err(), + Some(StatusCode::NAME_NOT_FOUND) + ); + assert_eq!( + binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").await.err(), + Some(StatusCode::NAME_NOT_FOUND) + ); + + // The service manager service isn't an ITest, so this must fail. + assert_eq!( + binder_tokio::get_interface::<dyn ITest>("manager").await.err(), + Some(StatusCode::BAD_TYPE) + ); + assert_eq!( + binder_tokio::get_interface::<dyn IATest<Tokio>>("manager").await.err(), + Some(StatusCode::BAD_TYPE) + ); } #[test] @@ -323,6 +416,10 @@ mod tests { binder::wait_for_interface::<dyn ITest>("manager").err(), Some(StatusCode::BAD_TYPE) ); + assert_eq!( + binder::wait_for_interface::<dyn IATest<Tokio>>("manager").err(), + Some(StatusCode::BAD_TYPE) + ); } #[test] @@ -334,6 +431,15 @@ mod tests { assert_eq!(test_client.test().unwrap(), "trivial_client_test"); } + #[tokio::test] + async fn trivial_client_async() { + let service_name = "trivial_client_test"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn IATest<Tokio>> = + binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + assert_eq!(test_client.test().await.unwrap(), "trivial_client_test"); + } + #[test] fn wait_for_trivial_client() { let service_name = "wait_for_trivial_client_test"; @@ -343,23 +449,47 @@ mod tests { assert_eq!(test_client.test().unwrap(), "wait_for_trivial_client_test"); } + #[tokio::test] + async fn wait_for_trivial_client_async() { + let service_name = "wait_for_trivial_client_test"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn IATest<Tokio>> = + binder_tokio::wait_for_interface(service_name).await.expect("Did not get manager binder service"); + assert_eq!(test_client.test().await.unwrap(), "wait_for_trivial_client_test"); + } + + fn get_expected_selinux_context() -> &'static str { + unsafe { + let mut out_ptr = ptr::null_mut(); + assert_eq!(selinux_sys::getcon(&mut out_ptr), 0); + assert!(!out_ptr.is_null()); + CStr::from_ptr(out_ptr) + .to_str() + .expect("context was invalid UTF-8") + } + } + #[test] fn get_selinux_context() { let service_name = "get_selinux_context"; let _process = ScopedServiceProcess::new(service_name); let test_client: Strong<dyn ITest> = binder::get_interface(service_name).expect("Did not get manager binder service"); - let expected_context = unsafe { - let mut out_ptr = ptr::null_mut(); - assert_eq!(selinux_sys::getcon(&mut out_ptr), 0); - assert!(!out_ptr.is_null()); - CStr::from_ptr(out_ptr) - }; assert_eq!( test_client.get_selinux_context().unwrap(), - expected_context - .to_str() - .expect("context was invalid UTF-8"), + get_expected_selinux_context() + ); + } + + #[tokio::test] + async fn get_selinux_context_async() { + let service_name = "get_selinux_context"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn IATest<Tokio>> = + binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + assert_eq!( + test_client.get_selinux_context().await.unwrap(), + get_expected_selinux_context() ); } diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index 66ba846c2d..1fc761e2c8 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -20,7 +20,7 @@ use binder::declare_binder_interface; use binder::parcel::ParcelFileDescriptor; use binder::{ - Binder, BinderFeatures, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status, + Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status, StatusCode, TransactionCode, }; @@ -111,8 +111,8 @@ impl ReadParcelTest for () {} fn on_transact( _service: &dyn ReadParcelTest, code: TransactionCode, - parcel: &Parcel, - reply: &mut Parcel, + parcel: &BorrowedParcel<'_>, + reply: &mut BorrowedParcel<'_>, ) -> Result<()> { match code { bindings::Transaction_TEST_BOOL => { diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index c8938998a6..63a4b2cc68 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -112,7 +112,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, BINDER_LIB_TEST_GETPID, BINDER_LIB_TEST_ECHO_VECTOR, - BINDER_LIB_TEST_REJECT_BUF, + BINDER_LIB_TEST_REJECT_OBJECTS, BINDER_LIB_TEST_CAN_GET_SID, }; @@ -1166,13 +1166,53 @@ TEST_F(BinderLibTest, BufRejected) { memcpy(parcelData, &obj, sizeof(obj)); data.setDataSize(sizeof(obj)); + EXPECT_EQ(data.objectsCount(), 1); + // Either the kernel should reject this transaction (if it's correct), but // if it's not, the server implementation should return an error if it // finds an object in the received Parcel. - EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply), + EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply), Not(StatusEq(NO_ERROR))); } +TEST_F(BinderLibTest, WeakRejected) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + auto binder = sp<BBinder>::make(); + wp<BBinder> wpBinder(binder); + flat_binder_object obj{ + .hdr = {.type = BINDER_TYPE_WEAK_BINDER}, + .flags = 0, + .binder = reinterpret_cast<uintptr_t>(wpBinder.get_refs()), + .cookie = reinterpret_cast<uintptr_t>(wpBinder.unsafe_get()), + }; + data.setDataCapacity(1024); + // Write a bogus object at offset 0 to get an entry in the offset table + data.writeFileDescriptor(0); + EXPECT_EQ(data.objectsCount(), 1); + uint8_t *parcelData = const_cast<uint8_t *>(data.data()); + // And now, overwrite it with the weak binder + memcpy(parcelData, &obj, sizeof(obj)); + data.setDataSize(sizeof(obj)); + + // a previous bug caused other objects to be released an extra time, so we + // test with an object that libbinder will actually try to release + EXPECT_EQ(OK, data.writeStrongBinder(sp<BBinder>::make())); + + EXPECT_EQ(data.objectsCount(), 2); + + // send it many times, since previous error was memory corruption, make it + // more likely that the server crashes + for (size_t i = 0; i < 100; i++) { + EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply), + StatusEq(BAD_VALUE)); + } + + EXPECT_THAT(server->pingBinder(), StatusEq(NO_ERROR)); +} + TEST_F(BinderLibTest, GotSid) { sp<IBinder> server = addServer(); @@ -1566,7 +1606,7 @@ public: reply->writeUint64Vector(vector); return NO_ERROR; } - case BINDER_LIB_TEST_REJECT_BUF: { + case BINDER_LIB_TEST_REJECT_OBJECTS: { return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR; } case BINDER_LIB_TEST_CAN_GET_SID: { diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 55ad3c6975..5a96b7835c 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -22,6 +22,7 @@ #include <aidl/IBinderRpcTest.h> #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/properties.h> #include <android/binder_auto_utils.h> #include <android/binder_libbinder.h> #include <binder/Binder.h> @@ -1514,7 +1515,17 @@ TEST(BinderRpc, Java) { auto socket = rpcServer->releaseServer(); auto keepAlive = sp<BBinder>::make(); - ASSERT_EQ(OK, binder->setRpcClientDebug(std::move(socket), keepAlive)); + auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive); + + if (!android::base::GetBoolProperty("ro.debuggable", false)) { + ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus) + << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable builds, " + "but get " + << statusToString(setRpcClientDebugStatus); + GTEST_SKIP(); + } + + ASSERT_EQ(OK, setRpcClientDebugStatus); auto rpcSession = RpcSession::make(); ASSERT_EQ(OK, rpcSession->setupInetClient("127.0.0.1", port)); diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index e4f57b0121..32406e599c 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -192,6 +192,8 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { // only reading one binder type for now PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder), PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder), + PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::os::IServiceManager>>, readStrongBinderVector), + PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::sp<android::os::IServiceManager>>>, readStrongBinderVector), PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), @@ -308,6 +310,15 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { status_t status = p.hasFileDescriptorsInRange(offset, length, &result); FUZZ_LOG() << " status: " << status << " result: " << result; }, + [] (const ::android::Parcel& p, uint8_t /* data */) { + FUZZ_LOG() << "about to call compareDataInRange() with status"; + size_t thisOffset = p.readUint32(); + size_t otherOffset = p.readUint32(); + size_t length = p.readUint32(); + int result; + status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result); + FUZZ_LOG() << " status: " << status << " result: " << result; + }, }; // clang-format on #pragma clang diagnostic pop diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index c0a762d1de..752fcbb34c 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -25,6 +25,7 @@ // TODO(b/142061461): parent class class SomeParcelable { public: + binder_status_t writeToParcel(AParcel* /*parcel*/) { return STATUS_OK; } binder_status_t readFromParcel(const AParcel* parcel) { return AParcel_readInt32(parcel, &mValue); } @@ -33,6 +34,41 @@ private: int32_t mValue = 0; }; +class ISomeInterface : public ::ndk::ICInterface { +public: + ISomeInterface() = default; + virtual ~ISomeInterface() = default; + static binder_status_t readFromParcel(const AParcel* parcel, + std::shared_ptr<ISomeInterface>* instance); +}; + +static binder_status_t onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) { + return STATUS_UNKNOWN_TRANSACTION; +} + +static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact); + +class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> { +public: + explicit BpSomeInterface(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {} + virtual ~BpSomeInterface() = default; +}; + +binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel, + std::shared_ptr<ISomeInterface>* instance) { + ::ndk::SpAIBinder binder; + binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR()); + if (status == STATUS_OK) { + if (AIBinder_associateClass(binder.get(), g_class)) { + *instance = std::static_pointer_cast<ISomeInterface>( + ::ndk::ICInterface::asInterface(binder.get())); + } else { + *instance = ::ndk::SharedRefBase::make<BpSomeInterface>(binder); + } + } + return status; +} + #define PARCEL_READ(T, FUN) \ [](const NdkParcelAdapter& p, uint8_t /*data*/) { \ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ @@ -100,6 +136,8 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::optional<std::vector<ndk::SpAIBinder>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<ndk::ScopedFileDescriptor>, ndk::AParcel_readVector), PARCEL_READ(std::optional<std::vector<ndk::ScopedFileDescriptor>>, ndk::AParcel_readVector), + PARCEL_READ(std::vector<std::shared_ptr<ISomeInterface>>, ndk::AParcel_readVector), + PARCEL_READ(std::optional<std::vector<std::shared_ptr<ISomeInterface>>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector), PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector), diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 1ae90f34f5..55703214a5 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -138,7 +138,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont mSize(width, height), mRequestedSize(mSize), mFormat(format), - mNextTransaction(nullptr) { + mSyncTransaction(nullptr) { createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout // explicitly so that dequeueBuffer will block @@ -290,13 +290,13 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, // 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 && + if (mWaitForTransactionCallback && mSyncTransaction == nullptr && currFrameNumber >= mLastAcquiredFrameNumber) { mWaitForTransactionCallback = false; flushShadowQueue(); } } else { - BQA_LOGE("Failed to find matching SurfaceControl in transaction callback"); + BQA_LOGE("Failed to find matching SurfaceControl in transactionCommittedCallback"); } } else { BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " @@ -346,7 +346,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.frameEventStats.dequeueReadyTime); } } else { - BQA_LOGE("Failed to find matching SurfaceControl in transaction callback"); + BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } } else { BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " @@ -407,20 +407,11 @@ void BLASTBufferQueue::releaseBufferCallback( // Release all buffers that are beyond the ones that we need to hold while (mPendingRelease.size() > numPendingBuffersToHold) { - const auto releaseBuffer = mPendingRelease.front(); + const auto releasedBuffer = mPendingRelease.front(); mPendingRelease.pop_front(); - auto it = mSubmitted.find(releaseBuffer.callbackId); - if (it == mSubmitted.end()) { - BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", - releaseBuffer.callbackId.to_string().c_str()); - return; - } - mNumAcquired--; - BQA_LOGV("released %s", releaseBuffer.callbackId.to_string().c_str()); - mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence); - mSubmitted.erase(it); + releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence); // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let - // onFrameAvailable handle processing them since it will merge with the nextTransaction. + // onFrameAvailable handle processing them since it will merge with the syncTransaction. if (!mWaitForTransactionCallback) { acquireNextBufferLocked(std::nullopt); } @@ -432,6 +423,20 @@ void BLASTBufferQueue::releaseBufferCallback( mCallbackCV.notify_all(); } +void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, + const sp<Fence>& releaseFence) { + auto it = mSubmitted.find(callbackId); + if (it == mSubmitted.end()) { + BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", + callbackId.to_string().c_str()); + return; + } + mNumAcquired--; + BQA_LOGV("released %s", callbackId.to_string().c_str()); + mBufferItemConsumer->releaseBuffer(it->second, releaseFence); + mSubmitted.erase(it); +} + void BLASTBufferQueue::acquireNextBufferLocked( const std::optional<SurfaceComposerClient::Transaction*> transaction) { ATRACE_CALL(); @@ -589,34 +594,57 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() { mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence); } +void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) { + if (mWaitForTransactionCallback && mNumFrameAvailable > 0) { + // 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 mSyncTransaction 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); + } +} + 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) { - 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(); + const bool syncTransactionSet = mSyncTransaction != nullptr; + BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); + + if (syncTransactionSet) { + bool mayNeedToWaitForBuffer = true; + // If we are going to re-use the same mSyncTransaction, release the buffer that may already + // be set in the Transaction. This is to allow us a free slot early to continue processing + // a new buffer. + if (!mAcquireSingleBuffer) { + auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl); + if (bufferData) { + BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64, + bufferData->frameNumber); + releaseBuffer(bufferData->releaseCallbackId, bufferData->acquireFence); + // Because we just released a buffer, we know there's no need to wait for a free + // buffer. + mayNeedToWaitForBuffer = false; } } - while (maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGD("waiting for free buffer."); - mCallbackCV.wait(_lock); + if (mayNeedToWaitForBuffer) { + flushAndWaitForFreeBuffer(_lock); } } @@ -625,12 +653,14 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_INT(mQueuedBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); - BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber, - boolToString(nextTransactionSet)); + BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber, + boolToString(syncTransactionSet)); - if (nextTransactionSet) { - acquireNextBufferLocked(std::move(mNextTransaction)); - mNextTransaction = nullptr; + if (syncTransactionSet) { + acquireNextBufferLocked(mSyncTransaction); + if (mAcquireSingleBuffer) { + mSyncTransaction = nullptr; + } mWaitForTransactionCallback = true; } else if (!mWaitForTransactionCallback) { acquireNextBufferLocked(std::nullopt); @@ -652,9 +682,11 @@ void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { mDequeueTimestamps.erase(bufferId); }; -void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) { +void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t, + bool acquireSingleBuffer) { std::lock_guard _lock{mMutex}; - mNextTransaction = t; + mSyncTransaction = t; + mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true; } bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 8edf60400c..a62697064b 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -71,6 +71,7 @@ static bool isPossiblyYUV(PixelFormat format) { case HAL_PIXEL_FORMAT_Y8: case HAL_PIXEL_FORMAT_Y16: case HAL_PIXEL_FORMAT_RAW16: + case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW_OPAQUE: case HAL_PIXEL_FORMAT_BLOB: diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index c986b82fd8..837967551d 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -33,10 +33,13 @@ namespace android { // using just a few large reads. static const size_t EVENT_BUFFER_SIZE = 100; +static constexpr nsecs_t WAITING_FOR_VSYNC_TIMEOUT = ms2ns(300); + DisplayEventDispatcher::DisplayEventDispatcher( const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::EventRegistrationFlags eventRegistration) - : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) { + : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false), + mLastVsyncCount(0), mLastScheduleVsyncTime(0) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); } @@ -86,6 +89,7 @@ status_t DisplayEventDispatcher::scheduleVsync() { } mWaitingForVsync = true; + mLastScheduleVsyncTime = systemTime(SYSTEM_TIME_MONOTONIC); } return OK; } @@ -124,9 +128,21 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount, vsyncEventData.id); mWaitingForVsync = false; + mLastVsyncCount = vsyncCount; dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData); } + if (mWaitingForVsync) { + const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + const nsecs_t vsyncScheduleDelay = currentTime - mLastScheduleVsyncTime; + if (vsyncScheduleDelay > WAITING_FOR_VSYNC_TIMEOUT) { + ALOGW("Vsync time out! vsyncScheduleDelay=%" PRId64 "ms", ns2ms(vsyncScheduleDelay)); + mWaitingForVsync = false; + dispatchVsync(currentTime, vsyncDisplayId /* displayId is not used */, + ++mLastVsyncCount, vsyncEventData /* empty data */); + } + } + return 1; // keep the callback } diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 30d19e3af4..b3647d6126 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -301,7 +301,7 @@ sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer( kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, + DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, "[GLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 3c8289fe2a..02950995af 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -1150,41 +1150,6 @@ public: return reply.readInt32(); } - status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override { - if (!outToken) return BAD_VALUE; - - Parcel data, reply; - status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (err != NO_ERROR) { - ALOGE("acquireFrameRateFlexibilityToken: failed writing interface token: %s (%d)", - strerror(-err), -err); - return err; - } - - err = remote()->transact(BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, data, - &reply); - if (err != NO_ERROR) { - ALOGE("acquireFrameRateFlexibilityToken: failed to transact: %s (%d)", strerror(-err), - err); - return err; - } - - err = reply.readInt32(); - if (err != NO_ERROR) { - ALOGE("acquireFrameRateFlexibilityToken: call failed: %s (%d)", strerror(-err), err); - return err; - } - - err = reply.readStrongBinder(outToken); - if (err != NO_ERROR) { - ALOGE("acquireFrameRateFlexibilityToken: failed reading binder token: %s (%d)", - strerror(-err), err); - return err; - } - - return NO_ERROR; - } - status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, const FrameTimelineInfo& frameTimelineInfo) override { Parcel data, reply; @@ -2073,16 +2038,6 @@ status_t BnSurfaceComposer::onTransact( reply->writeInt32(result); return NO_ERROR; } - case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> token; - status_t result = acquireFrameRateFlexibilityToken(&token); - reply->writeInt32(result); - if (result == NO_ERROR) { - reply->writeStrongBinder(token); - } - return NO_ERROR; - } case SET_FRAME_TIMELINE_INFO: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> binder; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 2713be060a..b139cf126c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -413,6 +413,14 @@ ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLock return callback; } +void TransactionCompletedListener::removeReleaseBufferCallback( + const ReleaseCallbackId& callbackId) { + { + std::scoped_lock<std::mutex> lock(mMutex); + popReleaseBufferCallbackLocked(callbackId); + } +} + // --------------------------------------------------------------------------- void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId); @@ -1307,6 +1315,28 @@ SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<Surfac return *this; } +std::optional<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffer( + const sp<SurfaceControl>& sc) { + layer_state_t* s = getLayerState(sc); + if (!s) { + return std::nullopt; + } + if (!(s->what & layer_state_t::eBufferChanged)) { + return std::nullopt; + } + + BufferData bufferData = s->bufferData; + + TransactionCompletedListener::getInstance()->removeReleaseBufferCallback( + bufferData.releaseCallbackId); + BufferData emptyBufferData; + s->what &= ~layer_state_t::eBufferChanged; + s->bufferData = emptyBufferData; + + mContainsBuffer = false; + return bufferData; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber, diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 3881620f3d..d76617373a 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -88,13 +88,13 @@ public: void onFrameDequeued(const uint64_t) override; void onFrameCancelled(const uint64_t) override; - virtual 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 transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats); + virtual void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount); - void setNextTransaction(SurfaceComposerClient::Transaction *t); + void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); @@ -132,6 +132,9 @@ private: void flushShadowQueue() REQUIRES(mMutex); void acquireAndReleaseBuffer() REQUIRES(mMutex); + void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence) + REQUIRES(mMutex); + void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock); std::string mName; // Represents the queued buffer count from buffer queue, @@ -208,7 +211,7 @@ private: sp<IGraphicBufferProducer> mProducer; sp<BLASTBufferItemConsumer> mBufferItemConsumer; - SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex); + SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex); std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>> mPendingTransactions GUARDED_BY(mMutex); @@ -239,7 +242,11 @@ private: std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); uint32_t mCurrentMaxAcquiredBufferCount; - bool mWaitForTransactionCallback = false; + bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false; + + // Flag to determine if syncTransaction should only acquire a single buffer and then clear or + // continue to acquire buffers until explicitly cleared + bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true; }; } // namespace android diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index 92c89b8bde..8a3a47644c 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -80,6 +80,8 @@ private: sp<Looper> mLooper; DisplayEventReceiver mReceiver; bool mWaitingForVsync; + uint32_t mLastVsyncCount; + nsecs_t mLastScheduleVsyncTime; std::vector<FrameRateOverride> mFrameRateOverrides; diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 408497dd36..e0183adbe6 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -512,14 +512,6 @@ public: int8_t compatibility, int8_t changeFrameRateStrategy) = 0; /* - * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired, - * surface flinger will freely switch between frame rates in any way it sees fit, regardless of - * the current restrictions applied by DisplayManager. This is useful to get consistent behavior - * for tests. Release the token by releasing the returned IBinder reference. - */ - virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0; - - /* * Sets the frame timeline vsync info received from choreographer that corresponds to next * buffer submitted on that surface. */ @@ -616,6 +608,7 @@ public: GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. SET_GAME_CONTENT_TYPE, SET_FRAME_RATE, + // Deprecated. Use DisplayManager.setShouldAlwaysRespectAppRequestedMode(true); ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, SET_FRAME_TIMELINE_INFO, ADD_TRANSACTION_TRACE_LISTENER, diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index e62c76ed80..e05c3646c6 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -493,6 +493,7 @@ public: const std::optional<uint64_t>& frameNumber = std::nullopt, const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID, ReleaseBufferCallback callback = nullptr); + std::optional<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc); Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, @@ -751,6 +752,8 @@ public: void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t currentMaxAcquiredBufferCount) override; + void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId); + // For Testing Only static void setInstance(const sp<TransactionCompletedListener>&); diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index b2d50482a5..48b8621be1 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -72,31 +72,30 @@ public: int height, int32_t format) : BLASTBufferQueue(name, surface, width, height, format) {} - void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence, - const std::vector<SurfaceControlStats>& stats) override { - BLASTBufferQueue::transactionCommittedCallback(latchTime, presentFence, stats); - + void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) override { + BLASTBufferQueue::transactionCallback(latchTime, presentFence, stats); uint64_t frameNumber = stats[0].frameEventStats.frameNumber; { std::unique_lock lock{frameNumberMutex}; - mLastTransactionCommittedFrameNumber = frameNumber; - mCommittedCV.notify_all(); + mLastTransactionFrameNumber = frameNumber; + mWaitForCallbackCV.notify_all(); } } void waitForCallback(int64_t frameNumber) { std::unique_lock lock{frameNumberMutex}; // Wait until all but one of the submitted buffers have been released. - while (mLastTransactionCommittedFrameNumber < frameNumber) { - mCommittedCV.wait(lock); + while (mLastTransactionFrameNumber < frameNumber) { + mWaitForCallbackCV.wait(lock); } } private: std::mutex frameNumberMutex; - std::condition_variable mCommittedCV; - int64_t mLastTransactionCommittedFrameNumber = -1; + std::condition_variable mWaitForCallbackCV; + int64_t mLastTransactionFrameNumber = -1; }; class BLASTBufferQueueHelper { @@ -110,15 +109,15 @@ public: mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888); } - void setNextTransaction(Transaction* next) { - mBlastBufferQueueAdapter->setNextTransaction(next); + void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) { + mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer); } int getWidth() { return mBlastBufferQueueAdapter->mSize.width; } int getHeight() { return mBlastBufferQueueAdapter->mSize.height; } - Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; } + Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; } sp<IGraphicBufferProducer> getIGraphicBufferProducer() { return mBlastBufferQueueAdapter->getIGraphicBufferProducer(); @@ -144,6 +143,11 @@ public: mBlastBufferQueueAdapter->waitForCallback(frameNumber); } + void validateNumFramesSubmitted(int64_t numFramesSubmitted) { + std::unique_lock lock{mBlastBufferQueueAdapter->mMutex}; + ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size()); + } + private: sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter; }; @@ -299,7 +303,7 @@ protected: auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr); - ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_TRUE(ret == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || ret == NO_ERROR); ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf)); uint32_t* bufData; @@ -338,7 +342,7 @@ TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl()); ASSERT_EQ(mDisplayWidth, adapter.getWidth()); ASSERT_EQ(mDisplayHeight, adapter.getHeight()); - ASSERT_EQ(nullptr, adapter.getNextTransaction()); + ASSERT_EQ(nullptr, adapter.getSyncTransaction()); } TEST_F(BLASTBufferQueueTest, Update) { @@ -359,11 +363,11 @@ TEST_F(BLASTBufferQueueTest, Update) { ASSERT_EQ(mDisplayHeight / 2, height); } -TEST_F(BLASTBufferQueueTest, SetNextTransaction) { +TEST_F(BLASTBufferQueueTest, SetSyncTransaction) { BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); - Transaction next; - adapter.setNextTransaction(&next); - ASSERT_EQ(&next, adapter.getNextTransaction()); + Transaction sync; + adapter.setSyncTransaction(&sync); + ASSERT_EQ(&sync, adapter.getSyncTransaction()); } TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) { @@ -802,8 +806,8 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { sp<IGraphicBufferProducer> igbProducer; setUpProducer(adapter, igbProducer); - Transaction next; - adapter.setNextTransaction(&next); + Transaction sync; + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); // queue non sync buffer, so this one should get blocked @@ -812,14 +816,14 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { queueBuffer(igbProducer, r, g, b, presentTimeDelay); CallbackHelper transactionCallback; - next.addTransactionCompletedCallback(transactionCallback.function, + sync.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()) .apply(); CallbackData callbackData; transactionCallback.getCallbackData(&callbackData); - // capture screen and verify that it is red + // capture screen and verify that it is green ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); ASSERT_NO_FATAL_FAILURE( checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); @@ -842,16 +846,16 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) { Transaction mainTransaction; - Transaction next; - adapter.setNextTransaction(&next); + Transaction sync; + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); // Expect 1 buffer to be released even before sending to SurfaceFlinger mProducerListener->waitOnNumberReleased(1); @@ -882,24 +886,24 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { Transaction mainTransaction; - Transaction next; + Transaction sync; // queue a sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - // queue another buffer without setting next transaction + // queue another buffer without setting sync transaction queueBuffer(igbProducer, 0, 0, 255, 0); // queue another sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 1 buffer to be released because the non sync transaction should merge // with the sync mProducerListener->waitOnNumberReleased(1); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); // Expect 2 buffers to be released due to merging the two syncs. mProducerListener->waitOnNumberReleased(2); @@ -930,26 +934,26 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { Transaction mainTransaction; - Transaction next; + Transaction sync; // queue a sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - // queue a few buffers without setting next transaction + // queue a few buffers without setting sync 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); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 3 buffers to be released because the non sync transactions should merge // with the sync mProducerListener->waitOnNumberReleased(3); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); // Expect 4 buffers to be released due to merging the two syncs. mProducerListener->waitOnNumberReleased(4); @@ -987,14 +991,14 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { // Send a buffer to SF queueBuffer(igbProducer, 0, 255, 0, 0); - Transaction next; + Transaction sync; // queue a sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - // queue a few buffers without setting next transaction + // queue a few buffers without setting sync transaction queueBuffer(igbProducer, 0, 0, 255, 0); queueBuffer(igbProducer, 0, 0, 255, 0); queueBuffer(igbProducer, 0, 0, 255, 0); @@ -1003,13 +1007,13 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { mainTransaction.apply(); // queue another sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 2 buffers to be released because the non sync transactions should merge // with the sync mProducerListener->waitOnNumberReleased(3); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); CallbackHelper transactionCallback; mainTransaction @@ -1026,6 +1030,45 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } +TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction next; + adapter.setSyncTransaction(&next, false); + queueBuffer(igbProducer, 0, 255, 0, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + // There should only be one frame submitted since the first frame will be released. + adapter.validateNumFramesSubmitted(1); + adapter.setSyncTransaction(nullptr); + + // 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, 255, 0, 0, presentTimeDelay); + + CallbackHelper transactionCallback; + next.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is blue + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + + mProducerListener->waitOnNumberReleased(2); + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + class TestProducerListener : public BnProducerListener { public: sp<IGraphicBufferProducer> mIgbp; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index a9f4d09838..b2baea6aba 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -885,10 +885,6 @@ public: return NO_ERROR; } - status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override { - return NO_ERROR; - } - status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& /*surface*/, const FrameTimelineInfo& /*frameTimelineInfo*/) override { return NO_ERROR; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index d018800c90..c7f77d42fe 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -66,11 +66,21 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) return transformedXy - transformedOrigin; } -bool shouldDisregardTranslation(uint32_t source) { +bool isFromSource(uint32_t source, uint32_t test) { + return (source & test) == test; +} + +bool shouldDisregardTransformation(uint32_t source) { + // Do not apply any transformations to axes from joysticks or touchpads. + return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || + isFromSource(source, AINPUT_SOURCE_CLASS_POSITION); +} + +bool shouldDisregardOffset(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; + return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER); } } // namespace @@ -707,7 +717,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { #endif bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) { - if (source & AINPUT_SOURCE_CLASS_POINTER) { + if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) { // Specifically excludes HOVER_MOVE and SCROLL. switch (action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: @@ -764,17 +774,31 @@ std::string MotionEvent::actionToString(int32_t action) { return android::base::StringPrintf("%" PRId32, action); } +// Apply the given transformation to the point without checking whether the entire transform +// should be disregarded altogether for the provided source. +static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform, + const vec2& xy) { + return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy) + : transform.transform(xy); +} + vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform, const vec2& xy) { - return shouldDisregardTranslation(source) ? transformWithoutTranslation(transform, xy) - : transform.transform(xy); + if (shouldDisregardTransformation(source)) { + return xy; + } + return calculateTransformedXYUnchecked(source, transform, xy); } float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform& transform, const PointerCoords& coords) { + if (shouldDisregardTransformation(source)) { + return coords.getAxisValue(axis); + } + if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = calculateTransformedXY(source, transform, coords.getXYValue()); + const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } @@ -796,17 +820,15 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, // --- FocusEvent --- -void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { +void FocusEvent::initialize(int32_t id, bool hasFocus) { InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; - mInTouchMode = inTouchMode; } void FocusEvent::initialize(const FocusEvent& from) { InputEvent::initialize(from); mHasFocus = from.mHasFocus; - mInTouchMode = from.mInTouchMode; } // --- CaptureEvent --- diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 69ae9a02ec..015bd81361 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -89,8 +89,15 @@ std::string getInputDeviceConfigurationFilePathByName( // Treblized input device config files will be located /product/usr, /system_ext/usr, // /odm/usr or /vendor/usr. - const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor", - getenv("ANDROID_ROOT")}; + // These files may also be in the com.android.input.config APEX. + const char* rootsForPartition[]{ + "/product", + "/system_ext", + "/odm", + "/vendor", + "/apex/com.android.input.config/etc", + getenv("ANDROID_ROOT"), + }; for (size_t i = 0; i < size(rootsForPartition); i++) { if (rootsForPartition[i] == nullptr) { continue; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 02a5a0807b..a065ce25f7 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -278,7 +278,6 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::FOCUS: { msg->body.focus.eventId = body.focus.eventId; msg->body.focus.hasFocus = body.focus.hasFocus; - msg->body.focus.inTouchMode = body.focus.inTouchMode; break; } case InputMessage::Type::CAPTURE: { @@ -622,13 +621,10 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } -status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, - bool inTouchMode) { +status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) { if (ATRACE_ENABLED()) { - std::string message = - StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", - mChannel->getName().c_str(), toString(hasFocus), - toString(inTouchMode)); + std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)", + mChannel->getName().c_str(), toString(hasFocus)); ATRACE_NAME(message.c_str()); } @@ -637,7 +633,6 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h msg.header.seq = seq; msg.body.focus.eventId = eventId; msg.body.focus.hasFocus = hasFocus; - msg.body.focus.inTouchMode = inTouchMode; return mChannel->sendMessage(&msg); } @@ -1371,8 +1366,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus, - msg->body.focus.inTouchMode); + event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus); } void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) { @@ -1491,9 +1485,8 @@ std::string InputConsumer::dump() const { break; } case InputMessage::Type::FOCUS: { - out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s", - toString(msg.body.focus.hasFocus), - toString(msg.body.focus.inTouchMode)); + out += android::base::StringPrintf("hasFocus=%s", + toString(msg.body.focus.hasFocus)); break; } case InputMessage::Type::CAPTURE: { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 1b594f142e..a92016ba3b 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -647,9 +647,8 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); } -MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, - const ui::Transform& transform, - const ui::Transform& rawTransform) { +MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy, + const ui::Transform& transform, const ui::Transform& rawTransform) { std::vector<PointerProperties> pointerProperties; pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER}); std::vector<PointerCoords> pointerCoords; @@ -660,8 +659,8 @@ MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, 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, - /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, + event.initialize(InputEvent::nextId(), /* deviceId */ 1, source, + /* displayId */ 0, INVALID_HMAC, action, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, transform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, @@ -670,6 +669,13 @@ MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, return event; } +MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, + const ui::Transform& transform, + const ui::Transform& rawTransform) { + return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy, + transform, rawTransform); +} + TEST_F(MotionEventTest, ApplyTransform) { // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform identity; @@ -708,16 +714,39 @@ TEST_F(MotionEventTest, ApplyTransform) { changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001); } +TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) { + constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD, + AMOTION_EVENT_ACTION_DOWN), + std::pair(AINPUT_SOURCE_JOYSTICK, + AMOTION_EVENT_ACTION_MOVE)}; + // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + + for (const auto& [source, action] : kNonTransformedSources) { + const MotionEvent event = + createMotionEvent(source, action, 60, 100, 0, 0, transform, transform); + + // These events should not be transformed in any way. + ASSERT_EQ(60, event.getX(0)); + ASSERT_EQ(100, event.getY(0)); + ASSERT_EQ(event.getRawX(0), event.getX(0)); + ASSERT_EQ(event.getRawY(0), event.getY(0)); + } +} + 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 transform(ui::Transform::ROT_90, 800, 400); - transform.set(transform.tx() + 20, transform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform); - event.setSource(source); + constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL, + AMOTION_EVENT_ACTION_DOWN), + std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, + AMOTION_EVENT_ACTION_MOVE)}; + // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + + for (const auto& [source, action] : kNonPointerSources) { + const MotionEvent event = + createMotionEvent(source, action, 60, 100, 42, 96, transform, transform); // Since this event comes from a non-pointer source, it should include rotation but not // translation/offset. diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 973194c8f6..05bc0bcbe8 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -290,10 +290,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { constexpr uint32_t seq = 15; int32_t eventId = InputEvent::nextId(); constexpr bool hasFocus = true; - constexpr bool inTouchMode = true; const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); - status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); + status = mPublisher->publishFocusEvent(seq, eventId, hasFocus); ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK"; uint32_t consumeSeq; @@ -309,7 +308,6 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(eventId, focusEvent->getId()); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); - EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 2f88704d63..b6a94764e5 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -84,8 +84,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); - CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5); - CHECK_OFFSET(InputMessage::Body::Focus, empty, 6); + CHECK_OFFSET(InputMessage::Body::Focus, empty, 5); CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0); CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4); diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 2f31888bf6..6882ea3090 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -191,7 +191,7 @@ sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() { // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, + DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 5823207517..c447d31606 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -172,6 +172,10 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709)); static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3)); static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR)); + static_assert(static_cast<int>(ADATASPACE_BT2020_HLG) == + static_cast<int>(HAL_DATASPACE_BT2020_HLG)); + static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_HLG) == + static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG)); if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || !isDataSpaceValid(window, dataSpace)) { diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 66f0e96873..30ac220052 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -528,6 +528,20 @@ enum ADataSpace { * components. */ ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL + + /** + * Hybrid Log Gamma encoding: + * + * Use full range, hybrid log gamma transfer and BT2020 standard. + */ + ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL + + /** + * ITU Hybrid Log Gamma encoding: + * + * Use limited range, hybrid log gamma transfer and BT2020 standard. + */ + ADATASPACE_BT2020_ITU_HLG = 302383104 // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED }; __END_DECLS diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index a62d2b9f85..ecfaef8928 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -40,6 +40,10 @@ cc_defaults { "libui", "libutils", ], + + static_libs: [ + "libtonemap", + ], local_include_dirs: ["include"], export_include_dirs: ["include"], } @@ -97,7 +101,7 @@ filegroup { "skia/filters/GaussianBlurFilter.cpp", "skia/filters/KawaseBlurFilter.cpp", "skia/filters/LinearEffect.cpp", - "skia/filters/StretchShaderFactory.cpp" + "skia/filters/StretchShaderFactory.cpp", ], } diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index a9ea690a7c..65d48951c6 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -26,55 +26,39 @@ namespace android { namespace renderengine { -std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { - RenderEngineType renderEngineType = args.renderEngineType; - +std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) { // Keep the ability to override by PROPERTIES: char prop[PROPERTY_VALUE_MAX]; property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "gles") == 0) { - renderEngineType = RenderEngineType::GLES; + args.renderEngineType = RenderEngineType::GLES; } if (strcmp(prop, "threaded") == 0) { - renderEngineType = RenderEngineType::THREADED; + args.renderEngineType = RenderEngineType::THREADED; } if (strcmp(prop, "skiagl") == 0) { - renderEngineType = RenderEngineType::SKIA_GL; + args.renderEngineType = RenderEngineType::SKIA_GL; } if (strcmp(prop, "skiaglthreaded") == 0) { - renderEngineType = RenderEngineType::SKIA_GL_THREADED; + args.renderEngineType = RenderEngineType::SKIA_GL_THREADED; } - switch (renderEngineType) { + switch (args.renderEngineType) { case RenderEngineType::THREADED: ALOGD("Threaded RenderEngine with GLES Backend"); return renderengine::threaded::RenderEngineThreaded::create( [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); }, - renderEngineType); + args.renderEngineType); case RenderEngineType::SKIA_GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); case RenderEngineType::SKIA_GL_THREADED: { - // These need to be recreated, since they are a constant reference, and we need to - // let SkiaRE know that it's running as threaded, and all GL operation will happen on - // the same thread. - RenderEngineCreationArgs skiaArgs = - RenderEngineCreationArgs::Builder() - .setPixelFormat(args.pixelFormat) - .setImageCacheSize(args.imageCacheSize) - .setUseColorManagerment(args.useColorManagement) - .setEnableProtectedContext(args.enableProtectedContext) - .setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) - .setSupportsBackgroundBlur(args.supportsBackgroundBlur) - .setContextPriority(args.contextPriority) - .setRenderEngineType(renderEngineType) - .build(); ALOGD("Threaded RenderEngine with SkiaGL Backend"); return renderengine::threaded::RenderEngineThreaded::create( - [skiaArgs]() { - return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs); + [args]() { + return android::renderengine::skia::SkiaGLRenderEngine::create(args); }, - renderEngineType); + args.renderEngineType); } case RenderEngineType::GLES: default: diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp new file mode 100644 index 0000000000..baa50544b7 --- /dev/null +++ b/libs/renderengine/benchmark/Android.bp @@ -0,0 +1,58 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_benchmark { + name: "librenderengine_bench", + defaults: [ + "skia_deps", + "surfaceflinger_defaults", + ], + srcs: [ + "main.cpp", + "Codec.cpp", + "Flags.cpp", + "RenderEngineBench.cpp", + ], + static_libs: [ + "librenderengine", + "libtonemap", + ], + cflags: [ + "-DLOG_TAG=\"RenderEngineBench\"", + ], + + shared_libs: [ + "libbase", + "libcutils", + "libjnigraphics", + "libgui", + "liblog", + "libnativewindow", + "libprocessgroup", + "libsync", + "libui", + "libutils", + ], + + data: ["resources/*"], +} diff --git a/libs/renderengine/benchmark/Codec.cpp b/libs/renderengine/benchmark/Codec.cpp new file mode 100644 index 0000000000..80e4fc4432 --- /dev/null +++ b/libs/renderengine/benchmark/Codec.cpp @@ -0,0 +1,136 @@ +/* + * 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 <RenderEngineBench.h> +#include <android/bitmap.h> +#include <android/data_space.h> +#include <android/imagedecoder.h> +#include <log/log.h> +#include <renderengine/ExternalTexture.h> +#include <renderengine/RenderEngine.h> +#include <sys/types.h> + +using namespace android; +using namespace android::renderengine; + +namespace { +struct DecoderDeleter { + void operator()(AImageDecoder* decoder) { AImageDecoder_delete(decoder); } +}; + +using AutoDecoderDeleter = std::unique_ptr<AImageDecoder, DecoderDeleter>; + +bool ok(int aImageDecoderResult, const char* path, const char* method) { + if (aImageDecoderResult == ANDROID_IMAGE_DECODER_SUCCESS) { + return true; + } + + ALOGE("Failed AImageDecoder_%s on '%s' with error '%s'", method, path, + AImageDecoder_resultToString(aImageDecoderResult)); + return false; +} +} // namespace + +namespace renderenginebench { + +void decode(const char* path, const sp<GraphicBuffer>& buffer) { + base::unique_fd fd{open(path, O_RDONLY)}; + if (fd.get() < 0) { + ALOGE("Failed to open %s", path); + return; + } + + AImageDecoder* decoder{nullptr}; + auto result = AImageDecoder_createFromFd(fd.get(), &decoder); + if (!ok(result, path, "createFromFd")) { + return; + } + + AutoDecoderDeleter deleter(decoder); + + LOG_ALWAYS_FATAL_IF(buffer->getWidth() <= 0 || buffer->getHeight() <= 0, + "Impossible buffer size!"); + auto width = static_cast<int32_t>(buffer->getWidth()); + auto height = static_cast<int32_t>(buffer->getHeight()); + result = AImageDecoder_setTargetSize(decoder, width, height); + if (!ok(result, path, "setTargetSize")) { + return; + } + + void* pixels{nullptr}; + int32_t stride{0}; + if (auto status = buffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &pixels, + nullptr /*outBytesPerPixel*/, &stride); + status < 0) { + ALOGE("Failed to lock pixels!"); + return; + } + + result = AImageDecoder_decodeImage(decoder, pixels, static_cast<size_t>(stride), + static_cast<size_t>(stride * height)); + if (auto status = buffer->unlock(); status < 0) { + ALOGE("Failed to unlock pixels!"); + } + + // For the side effect of logging. + (void)ok(result, path, "decodeImage"); +} + +void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer) { + base::unique_fd fd{open(path, O_WRONLY | O_CREAT, S_IWUSR)}; + if (fd.get() < 0) { + ALOGE("Failed to open %s", path); + return; + } + + void* pixels{nullptr}; + int32_t stride{0}; + if (auto status = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels, + nullptr /*outBytesPerPixel*/, &stride); + status < 0) { + ALOGE("Failed to lock pixels!"); + return; + } + + AndroidBitmapInfo info{ + .width = buffer->getWidth(), + .height = buffer->getHeight(), + .stride = static_cast<uint32_t>(stride), + .format = ANDROID_BITMAP_FORMAT_RGBA_8888, + .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE, + }; + int result = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels, + ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, 80, &fd, + [](void* fdPtr, const void* data, size_t size) -> bool { + const ssize_t bytesWritten = + write(reinterpret_cast<base::unique_fd*>(fdPtr) + ->get(), + data, size); + return bytesWritten > 0 && + static_cast<size_t>(bytesWritten) == size; + }); + if (result == ANDROID_BITMAP_RESULT_SUCCESS) { + ALOGD("Successfully encoded to '%s'", path); + } else { + ALOGE("Failed to encode to %s with error %d", path, result); + } + + if (auto status = buffer->unlock(); status < 0) { + ALOGE("Failed to unlock pixels!"); + } +} + +} // namespace renderenginebench diff --git a/libs/renderengine/benchmark/Flags.cpp b/libs/renderengine/benchmark/Flags.cpp new file mode 100644 index 0000000000..c5d51563f4 --- /dev/null +++ b/libs/renderengine/benchmark/Flags.cpp @@ -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. + */ + +#include <log/log.h> +#include <stdio.h> +#include <string.h> + +namespace { +bool gSave = false; +} + +namespace renderenginebench { + +void parseFlagsForHelp(int argc, char** argv) { + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--help")) { + printf("RenderEngineBench-specific flags:\n"); + printf("[--save]: Save the output to the device to confirm drawing result.\n"); + break; + } + } +} + +void parseFlags(int argc, char** argv) { + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--save")) { + gSave = true; + } + } +} + +bool save() { + return gSave; +} +} // namespace renderenginebench diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp new file mode 100644 index 0000000000..6c8f8e878b --- /dev/null +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -0,0 +1,259 @@ +/* + * 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 <RenderEngineBench.h> +#include <android-base/file.h> +#include <benchmark/benchmark.h> +#include <gui/SurfaceComposerClient.h> +#include <log/log.h> +#include <renderengine/ExternalTexture.h> +#include <renderengine/LayerSettings.h> +#include <renderengine/RenderEngine.h> + +#include <mutex> + +using namespace android; +using namespace android::renderengine; + +/////////////////////////////////////////////////////////////////////////////// +// Helpers for Benchmark::Apply +/////////////////////////////////////////////////////////////////////////////// + +std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) { + switch (type) { + case RenderEngine::RenderEngineType::SKIA_GL_THREADED: + return "skiaglthreaded"; + case RenderEngine::RenderEngineType::SKIA_GL: + return "skiagl"; + case RenderEngine::RenderEngineType::GLES: + case RenderEngine::RenderEngineType::THREADED: + LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?"); + return "unused"; + } +} + +/** + * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a + * Benchmark which specifies which RenderEngineType it uses. + * + * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make + * it obvious which version is being run. + * + * @param b The benchmark family + * @param type The type of RenderEngine to use. + */ +static void AddRenderEngineType(benchmark::internal::Benchmark* b, + RenderEngine::RenderEngineType type) { + b->Arg(static_cast<int64_t>(type)); + b->ArgName(RenderEngineTypeName(type)); +} + +/** + * Run a benchmark once using SKIA_GL_THREADED. + */ +static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) { + AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED); +} + +/////////////////////////////////////////////////////////////////////////////// +// Helpers for calling drawLayers +/////////////////////////////////////////////////////////////////////////////// + +std::pair<uint32_t, uint32_t> getDisplaySize() { + // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed + // to GraphicBuffer, which wants uint32_t. + static uint32_t width, height; + std::once_flag once; + std::call_once(once, []() { + auto surfaceComposerClient = SurfaceComposerClient::getDefault(); + auto displayToken = surfaceComposerClient->getInternalDisplayToken(); + ui::DisplayMode displayMode; + if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) { + LOG_ALWAYS_FATAL("Failed to get active display mode!"); + } + auto w = displayMode.resolution.width; + auto h = displayMode.resolution.height; + LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!"); + width = static_cast<uint32_t>(w); + height = static_cast<uint32_t>(h); + }); + return std::pair<uint32_t, uint32_t>(width, height); +} + +// This value doesn't matter, as it's not read. TODO(b/199918329): Once we remove +// GLESRenderEngine we can remove this, too. +static constexpr const bool kUseFrameBufferCache = false; + +static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) { + auto args = RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setEnableProtectedContext(true) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(RenderEngine::ContextPriority::REALTIME) + .setRenderEngineType(type) + .setUseColorManagerment(true) + .build(); + return RenderEngine::create(args); +} + +static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_t width, + uint32_t height, + uint64_t extraUsageFlags = 0, + std::string name = "output") { + return std::make_shared<ExternalTexture>(new GraphicBuffer(width, height, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE | + extraUsageFlags, + std::move(name)), + re, + ExternalTexture::Usage::READABLE | + ExternalTexture::Usage::WRITEABLE); +} + +static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re, + std::shared_ptr<ExternalTexture> original, + uint64_t extraUsageFlags, std::string name) { + const uint32_t width = original->getBuffer()->getWidth(); + const uint32_t height = original->getBuffer()->getHeight(); + auto texture = allocateBuffer(re, width, height, extraUsageFlags, name); + + const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height)); + DisplaySettings display{ + .physicalDisplay = displayRect, + .clip = displayRect, + .maxLuminance = 500, + }; + + const FloatRect layerRect(0, 0, width, height); + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = layerRect, + }, + .source = + PixelSource{ + .buffer = + Buffer{ + .buffer = original, + }, + }, + .alpha = half(1.0f), + }; + auto layers = std::vector<LayerSettings>{layer}; + + auto [status, drawFence] = + re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()).get(); + sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence)); + waitFence->waitForever(LOG_TAG); + return texture; +} + +/** + * Helper for timing calls to drawLayers. + * + * Caller needs to create RenderEngine and the LayerSettings, and this takes + * care of setting up the display, starting and stopping the timer, calling + * drawLayers, and saving (if --save is used). + * + * This times both the CPU and GPU work initiated by drawLayers. All work done + * outside of the for loop is excluded from the timing measurements. + */ +static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers, + benchmark::State& benchState, const char* saveFileName) { + auto [width, height] = getDisplaySize(); + auto outputBuffer = allocateBuffer(re, width, height); + + const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height)); + DisplaySettings display{ + .physicalDisplay = displayRect, + .clip = displayRect, + .maxLuminance = 500, + }; + + // This loop starts and stops the timer. + for (auto _ : benchState) { + auto [status, drawFence] = re.drawLayers(display, layers, outputBuffer, + kUseFrameBufferCache, base::unique_fd()) + .get(); + sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence)); + waitFence->waitForever(LOG_TAG); + } + + if (renderenginebench::save() && saveFileName) { + // Copy to a CPU-accessible buffer so we can encode it. + outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode"); + + std::string outFile = base::GetExecutableDirectory(); + outFile.append("/"); + outFile.append(saveFileName); + outFile.append(".jpg"); + renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer()); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Benchmarks +/////////////////////////////////////////////////////////////////////////////// + +void BM_blur(benchmark::State& benchState) { + auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range())); + + // Initially use cpu access so we can decode into it with AImageDecoder. + auto [width, height] = getDisplaySize(); + auto srcBuffer = + allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source"); + { + std::string srcImage = base::GetExecutableDirectory(); + srcImage.append("/resources/homescreen.png"); + renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer()); + + // Now copy into GPU-only buffer for more realistic timing. + srcBuffer = copyBuffer(*re, srcBuffer, 0, "source"); + } + + const FloatRect layerRect(0, 0, width, height); + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = layerRect, + }, + .source = + PixelSource{ + .buffer = + Buffer{ + .buffer = srcBuffer, + }, + }, + .alpha = half(1.0f), + }; + LayerSettings blurLayer{ + .geometry = + Geometry{ + .boundaries = layerRect, + }, + .alpha = half(1.0f), + .skipContentDraw = true, + .backgroundBlurRadius = 60, + }; + + auto layers = std::vector<LayerSettings>{layer, blurLayer}; + benchDrawLayers(*re, layers, benchState, "blurred"); +} + +BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded); diff --git a/libs/renderengine/benchmark/RenderEngineBench.h b/libs/renderengine/benchmark/RenderEngineBench.h new file mode 100644 index 0000000000..1a25d77f00 --- /dev/null +++ b/libs/renderengine/benchmark/RenderEngineBench.h @@ -0,0 +1,65 @@ +/* + * 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 <ui/GraphicBuffer.h> + +using namespace android; + +/** + * Utilities for running benchmarks. + */ +namespace renderenginebench { +/** + * Parse RenderEngineBench-specific flags from the command line. + * + * --save Save the output buffer to a file to verify that it drew as + * expected. + */ +void parseFlags(int argc, char** argv); + +/** + * Parse flags for '--help' + */ +void parseFlagsForHelp(int argc, char** argv); + +/** + * Whether to save the drawing result to a file. + * + * True if --save was used on the command line. + */ +bool save(); + +/** + * Decode the image at 'path' into 'buffer'. + * + * Currently only used for debugging. The image will be scaled to fit the + * buffer if necessary. + * + * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888. + * + * @param path Relative to the directory holding the executable. + */ +void decode(const char* path, const sp<GraphicBuffer>& buffer); + +/** + * Encode the buffer to a jpeg. + * + * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888. + * + * @param path Relative to the directory holding the executable. + */ +void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer); +} // namespace renderenginebench diff --git a/libs/renderengine/benchmark/main.cpp b/libs/renderengine/benchmark/main.cpp new file mode 100644 index 0000000000..7a62853c9f --- /dev/null +++ b/libs/renderengine/benchmark/main.cpp @@ -0,0 +1,32 @@ +/* + * 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 <RenderEngineBench.h> +#include <benchmark/benchmark.h> + +int main(int argc, char** argv) { + // Initialize will exit if it sees '--help', so check for it and print info + // about our flags first. + renderenginebench::parseFlagsForHelp(argc, argv); + benchmark::Initialize(&argc, argv); + + // Calling this separately from parseFlagsForHelp prevents collisions with + // google-benchmark's flags, since Initialize will consume and remove flags + // it recognizes. + renderenginebench::parseFlags(argc, argv); + benchmark::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/libs/renderengine/benchmark/resources/homescreen.png b/libs/renderengine/benchmark/resources/homescreen.png Binary files differnew file mode 100644 index 0000000000..997b72d7a3 --- /dev/null +++ b/libs/renderengine/benchmark/resources/homescreen.png diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index b9cc6485e5..6b85c57069 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -99,7 +99,7 @@ public: SKIA_GL_THREADED = 4, }; - static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); + static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args); virtual ~RenderEngine() = 0; diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp index 73dadef166..c3a5a60e13 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -19,6 +19,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <SkString.h> +#include <tonemap/tonemap.h> #include <utils/Trace.h> #include <optional> @@ -32,6 +33,11 @@ namespace android { namespace renderengine { namespace skia { +static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace( + ui::Dataspace dataspace) { + return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace); +} + static void generateEOTF(ui::Dataspace dataspace, SkString& shader) { switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: @@ -108,7 +114,8 @@ static void generateXYZTransforms(SkString& shader) { } // Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits]) -static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) { +static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, + ui::Dataspace outputDataspace, SkString& shader) { switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: shader.append(R"( @@ -125,158 +132,26 @@ static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkStrin )"); break; default: - shader.append(R"( - float3 ScaleLuminance(float3 xyz) { - return xyz * in_inputMaxLuminance; - } - )"); - break; - } -} - -static void generateToneMapInterpolation(ui::Dataspace inputDataspace, - ui::Dataspace outputDataspace, SkString& shader) { - switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { - case HAL_DATASPACE_TRANSFER_ST2084: - case HAL_DATASPACE_TRANSFER_HLG: switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: - shader.append(R"( - float3 ToneMap(float3 xyz) { - return xyz; - } - )"); - break; case HAL_DATASPACE_TRANSFER_HLG: - // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so - // we'll clamp the luminance range in case we're mapping from PQ input to HLG - // output. + // SDR -> HDR tonemap shader.append(R"( - float3 ToneMap(float3 xyz) { - return clamp(xyz, 0.0, 1000.0); + float3 ScaleLuminance(float3 xyz) { + return xyz * in_libtonemap_inputMaxLuminance; } )"); break; default: - // Here we're mapping from HDR to SDR content, so interpolate using a Hermitian - // polynomial onto the smaller luminance range. - shader.append(R"( - float3 ToneMap(float3 xyz) { - float maxInLumi = in_inputMaxLuminance; - float maxOutLumi = in_displayMaxLuminance; - - float nits = xyz.y; - - // if the max input luminance is less than what we can output then - // no tone mapping is needed as all color values will be in range. - if (maxInLumi <= maxOutLumi) { - return xyz; - } else { - - // three control points - const float x0 = 10.0; - const float y0 = 17.0; - float x1 = maxOutLumi * 0.75; - float y1 = x1; - float x2 = x1 + (maxInLumi - x1) / 2.0; - float y2 = y1 + (maxOutLumi - y1) * 0.75; - - // horizontal distances between the last three control points - float h12 = x2 - x1; - float h23 = maxInLumi - x2; - // tangents at the last three control points - float m1 = (y2 - y1) / h12; - float m3 = (maxOutLumi - y2) / h23; - float m2 = (m1 + m3) / 2.0; - - if (nits < x0) { - // scale [0.0, x0] to [0.0, y0] linearly - float slope = y0 / x0; - return xyz * slope; - } else if (nits < x1) { - // scale [x0, x1] to [y0, y1] linearly - float slope = (y1 - y0) / (x1 - x0); - nits = y0 + (nits - x0) * slope; - } else if (nits < x2) { - // scale [x1, x2] to [y1, y2] using Hermite interp - float t = (nits - x1) / h12; - nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) + - (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; - } else { - // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp - float t = (nits - x2) / h23; - nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + - (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t; - } - } - - // color.y is greater than x0 and is thus non-zero - return xyz * (nits / xyz.y); - } - )"); - break; - } - break; - default: - switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { - case HAL_DATASPACE_TRANSFER_ST2084: - case HAL_DATASPACE_TRANSFER_HLG: - // Map from SDR onto an HDR output buffer - // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto - // [0, maxOutLumi] which is hard-coded to be 3000 nits. + // Input and output are both SDR, so no tone-mapping is expected so + // no-op the luminance normalization. shader.append(R"( - float3 ToneMap(float3 xyz) { - const float maxOutLumi = 3000.0; - - const float x0 = 5.0; - const float y0 = 2.5; - float x1 = in_displayMaxLuminance * 0.7; - float y1 = maxOutLumi * 0.15; - float x2 = in_displayMaxLuminance * 0.9; - float y2 = maxOutLumi * 0.45; - float x3 = in_displayMaxLuminance; - float y3 = maxOutLumi; - - float c1 = y1 / 3.0; - float c2 = y2 / 2.0; - float c3 = y3 / 1.5; - - float nits = xyz.y; - - if (nits <= x0) { - // scale [0.0, x0] to [0.0, y0] linearly - float slope = y0 / x0; - return xyz * slope; - } else if (nits <= x1) { - // scale [x0, x1] to [y0, y1] using a curve - float t = (nits - x0) / (x1 - x0); - nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1; - } else if (nits <= x2) { - // scale [x1, x2] to [y1, y2] using a curve - float t = (nits - x1) / (x2 - x1); - nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2; - } else { - // scale [x2, x3] to [y2, y3] using a curve - float t = (nits - x2) / (x3 - x2); - nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3; + float3 ScaleLuminance(float3 xyz) { + return xyz * in_libtonemap_displayMaxLuminance; } - - // xyz.y is greater than x0 and is thus non-zero - return xyz * (nits / xyz.y); - } - )"); - break; - default: - // For completeness, this is tone-mapping from SDR to SDR, where this is just a - // no-op. - shader.append(R"( - float3 ToneMap(float3 xyz) { - return xyz; - } - )"); + )"); break; } - break; } } @@ -300,7 +175,7 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, default: shader.append(R"( float3 NormalizeLuminance(float3 xyz) { - return xyz / in_displayMaxLuminance; + return xyz / in_libtonemap_displayMaxLuminance; } )"); break; @@ -309,19 +184,22 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, SkString& shader) { - // Input uniforms - shader.append(R"( - uniform float in_displayMaxLuminance; - uniform float in_inputMaxLuminance; - )"); + shader.append(tonemap::getToneMapper() + ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace), + toAidlDataspace(outputDataspace)) + .c_str()); - generateLuminanceScalesForOOTF(inputDataspace, shader); - generateToneMapInterpolation(inputDataspace, outputDataspace, shader); + generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader); generateLuminanceNormalizationForOOTF(outputDataspace, shader); shader.append(R"( - float3 OOTF(float3 xyz) { - return NormalizeLuminance(ToneMap(ScaleLuminance(xyz))); + float3 OOTF(float3 linearRGB, float3 xyz) { + float3 scaledLinearRGB = ScaleLuminance(linearRGB); + float3 scaledXYZ = ScaleLuminance(xyz); + + float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ); + + return NormalizeLuminance(scaledXYZ * gain); } )"); } @@ -399,7 +277,9 @@ static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) )"); } shader.append(R"( - c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb))))); + float3 linearRGB = EOTF(c.rgb); + float3 xyz = ToXYZ(linearRGB); + c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz))); )"); if (undoPremultipliedAlpha) { shader.append(R"( @@ -465,11 +345,20 @@ sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEff colorTransform * mat4(outputColorSpace.getXYZtoRGB()); } - effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance; - // If the input luminance is unknown, use display luminance (aka, no-op any luminance changes) - // This will be the case for eg screenshots in addition to uncalibrated displays - effectBuilder.uniform("in_inputMaxLuminance") = - maxLuminance > 0 ? maxLuminance : maxDisplayLuminance; + tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance, + // If the input luminance is unknown, use display luminance (aka, + // no-op any luminance changes) + // This will be the case for eg screenshots in addition to + // uncalibrated displays + .contentMaxLuminance = + maxLuminance > 0 ? maxLuminance : maxDisplayLuminance}; + + const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata); + + for (const auto& uniform : uniforms) { + effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size()); + } + return effectBuilder.makeShader(nullptr, false); } diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index d0e19dd4e2..52b6c8ff72 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -23,7 +23,10 @@ package { cc_test { name: "librenderengine_test", - defaults: ["skia_deps", "surfaceflinger_defaults"], + defaults: [ + "skia_deps", + "surfaceflinger_defaults", + ], test_suites: ["device-tests"], srcs: [ "RenderEngineTest.cpp", @@ -36,6 +39,7 @@ cc_test { "libgmock", "librenderengine", "librenderengine_mocks", + "libtonemap", ], shared_libs: [ diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp new file mode 100644 index 0000000000..231a342852 --- /dev/null +++ b/libs/tonemap/Android.bp @@ -0,0 +1,37 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_static { + name: "libtonemap", + vendor_available: true, + + export_include_dirs: ["include"], + local_include_dirs: ["include"], + + shared_libs: [ + "android.hardware.graphics.common-V3-ndk", + ], + srcs: [ + "tonemap.cpp", + ], +} diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS new file mode 100644 index 0000000000..6d91da3bd2 --- /dev/null +++ b/libs/tonemap/OWNERS @@ -0,0 +1,4 @@ +alecmouri@google.com +jreck@google.com +sallyqi@google.com +scroggo@google.com
\ No newline at end of file diff --git a/libs/tonemap/TEST_MAPPING b/libs/tonemap/TEST_MAPPING new file mode 100644 index 0000000000..00f83baaa9 --- /dev/null +++ b/libs/tonemap/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "librenderengine_test" + }, + { + "name": "libtonemap_test" + } + ] +} diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h new file mode 100644 index 0000000000..d350e160ca --- /dev/null +++ b/libs/tonemap/include/tonemap/tonemap.h @@ -0,0 +1,103 @@ +/* + * 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 <aidl/android/hardware/graphics/common/Dataspace.h> + +#include <string> +#include <vector> + +namespace android::tonemap { + +// Describes a shader uniform +// The shader uniform is intended to be passed into a SkRuntimeShaderBuilder, i.e.: +// +// SkRuntimeShaderBuilder builder; +// builder.uniform(<uniform name>).set(<uniform value>.data(), <uniform value>.size()); +struct ShaderUniform { + // The name of the uniform, used for binding into a shader. + // The shader must contain a uniform whose name matches this. + std::string name; + + // The value for the uniform, which should be bound to the uniform identified by <name> + std::vector<uint8_t> value; +}; + +// Describes metadata which may be used for constructing the shader uniforms. +// This metadata should not be used for manipulating the source code of the shader program directly, +// as otherwise caching by other system of these shaders may break. +struct Metadata { + float displayMaxLuminance = 0.0; + float contentMaxLuminance = 0.0; +}; + +class ToneMapper { +public: + virtual ~ToneMapper() {} + // Constructs a tonemap shader whose shader language is SkSL + // + // The returned shader string *must* contain a function with the following signature: + // float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz); + // + // The arguments are: + // * linearRGB is the absolute nits of the RGB pixels in linear space + // * xyz is linearRGB converted into XYZ + // + // libtonemap_LookupTonemapGain() returns a float representing the amount by which to scale the + // absolute nits of the pixels. This function may be plugged into any existing SkSL shader, and + // is expected to look something like this: + // + // vec3 rgb = ...; + // // apply the EOTF based on the incoming dataspace to convert to linear nits. + // vec3 linearRGB = applyEOTF(rgb); + // // apply a RGB->XYZ matrix float3 + // vec3 xyz = toXYZ(linearRGB); + // // Scale the luminance based on the content standard + // vec3 absoluteRGB = ScaleLuminance(linearRGB); + // vec3 absoluteXYZ = ScaleLuminance(xyz); + // float gain = libtonemap_LookupTonemapGain(absoluteRGB, absoluteXYZ); + // // Normalize the luminance back down to a [0, 1] range + // xyz = NormalizeLuminance(absoluteXYZ * gain); + // // apply a XYZ->RGB matrix and apply the output OETf. + // vec3 finalColor = applyOETF(ToRGB(xyz)); + // ... + // + // Helper methods in this shader should be prefixed with "libtonemap_". Accordingly, libraries + // which consume this shader must *not* contain any methods prefixed with "libtonemap_" to + // guarantee that there are no conflicts in name resolution. + virtual std::string generateTonemapGainShaderSkSL( + aidl::android::hardware::graphics::common::Dataspace sourceDataspace, + aidl::android::hardware::graphics::common::Dataspace destinationDataspace) = 0; + + // Constructs uniform descriptions that correspond to those that are generated for the tonemap + // shader. Uniforms must be prefixed with "in_libtonemap_". Libraries which consume this shader + // must not bind any new uniforms that begin with this prefix. + // + // Downstream shaders may assume the existence of the uniform in_libtonemap_displayMaxLuminance + // and in_libtonemap_inputMaxLuminance, in order to assist with scaling and normalizing + // luminance as described in the documentation for generateTonemapGainShaderSkSL(). That is, + // shaders plugging in a tone-mapping shader returned by generateTonemapGainShaderSkSL() may + // assume that there are predefined floats in_libtonemap_displayMaxLuminance and + // in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader. + virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0; +}; + +// Retrieves a tonemapper instance. +// This instance is globally constructed. +ToneMapper* getToneMapper(); + +} // namespace android::tonemap
\ No newline at end of file diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp new file mode 100644 index 0000000000..e58d519224 --- /dev/null +++ b/libs/tonemap/tests/Android.bp @@ -0,0 +1,38 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "libtonemap_test", + test_suites: ["device-tests"], + srcs: [ + "tonemap_test.cpp", + ], + shared_libs: [ + "android.hardware.graphics.common-V3-ndk", + ], + static_libs: [ + "libgmock", + "libgtest", + "libtonemap", + ], +} diff --git a/libs/tonemap/tests/tonemap_test.cpp b/libs/tonemap/tests/tonemap_test.cpp new file mode 100644 index 0000000000..7a7958f58f --- /dev/null +++ b/libs/tonemap/tests/tonemap_test.cpp @@ -0,0 +1,76 @@ +/* + * 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 <gmock/gmock.h> +#include <gtest/gtest.h> +#include <tonemap/tonemap.h> +#include <cmath> + +namespace android { + +using testing::HasSubstr; + +struct TonemapTest : public ::testing::Test {}; + +TEST_F(TonemapTest, generateShaderSkSLUniforms_containsDefaultUniforms) { + static const constexpr float kDisplayMaxLuminance = 1.f; + static const constexpr float kContentMaxLuminance = 2.f; + tonemap::Metadata metadata{.displayMaxLuminance = kDisplayMaxLuminance, + .contentMaxLuminance = kContentMaxLuminance}; + const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata); + + ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) { + return data.name == "in_libtonemap_displayMaxLuminance"; + })); + ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) { + return data.name == "in_libtonemap_inputMaxLuminance"; + })); + + // Smoke check that metadata values are "real", specifically that they're non-zero and actually + // numbers. This is to help avoid shaders using these uniforms from dividing by zero or other + // catastrophic errors. + const auto& displayLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) { + return data.name == "in_libtonemap_displayMaxLuminance"; + })->value; + + float displayLumFloat = 0.f; + std::memcpy(&displayLumFloat, displayLum.data(), displayLum.size()); + EXPECT_FALSE(std::isnan(displayLumFloat)); + EXPECT_GT(displayLumFloat, 0); + + const auto& contentLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) { + return data.name == "in_libtonemap_inputMaxLuminance"; + })->value; + + float contentLumFloat = 0.f; + std::memcpy(&contentLumFloat, contentLum.data(), contentLum.size()); + EXPECT_FALSE(std::isnan(contentLumFloat)); + EXPECT_GT(contentLumFloat, 0); +} + +TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) { + const auto shader = + tonemap::getToneMapper() + ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common:: + Dataspace::BT2020_ITU_PQ, + aidl::android::hardware::graphics::common:: + Dataspace::DISPLAY_P3); + + // Other tests such as librenderengine_test will plug in the shader to check compilation. + EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)")); +} + +} // namespace android diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp new file mode 100644 index 0000000000..2cec773eb3 --- /dev/null +++ b/libs/tonemap/tonemap.cpp @@ -0,0 +1,410 @@ +/* + * 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 <tonemap/tonemap.h> + +#include <cstdint> +#include <mutex> +#include <type_traits> + +namespace android::tonemap { + +namespace { + +// Flag containing the variant of tone map algorithm to use. +enum class ToneMapAlgorithm { + AndroidO, // Default algorithm in place since Android O, + Android13, // Algorithm used in Android 13. +}; + +static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::Android13; + +static const constexpr auto kTransferMask = + static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_MASK); +static const constexpr auto kTransferST2084 = + static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_ST2084); +static const constexpr auto kTransferHLG = + static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_HLG); + +template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true> +std::vector<uint8_t> buildUniformValue(T value) { + std::vector<uint8_t> result; + result.resize(sizeof(value)); + std::memcpy(result.data(), &value, sizeof(value)); + return result; +} + +class ToneMapperO : public ToneMapper { +public: + std::string generateTonemapGainShaderSkSL( + aidl::android::hardware::graphics::common::Dataspace sourceDataspace, + aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override { + const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); + const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace); + + std::string program; + // Define required uniforms + program.append(R"( + uniform float in_libtonemap_displayMaxLuminance; + uniform float in_libtonemap_inputMaxLuminance; + )"); + switch (sourceDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + program.append(R"( + float libtonemap_ToneMapTargetNits(vec3 xyz) { + return xyz.y; + } + )"); + break; + case kTransferHLG: + // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so + // we'll clamp the luminance range in case we're mapping from PQ input to + // HLG output. + program.append(R"( + float libtonemap_ToneMapTargetNits(vec3 xyz) { + return clamp(xyz.y, 0.0, 1000.0); + } + )"); + break; + default: + // Here we're mapping from HDR to SDR content, so interpolate using a + // Hermitian polynomial onto the smaller luminance range. + program.append(R"( + float libtonemap_ToneMapTargetNits(vec3 xyz) { + float maxInLumi = in_libtonemap_inputMaxLuminance; + float maxOutLumi = in_libtonemap_displayMaxLuminance; + + float nits = xyz.y; + + // if the max input luminance is less than what we can + // output then no tone mapping is needed as all color + // values will be in range. + if (maxInLumi <= maxOutLumi) { + return xyz.y; + } else { + + // three control points + const float x0 = 10.0; + const float y0 = 17.0; + float x1 = maxOutLumi * 0.75; + float y1 = x1; + float x2 = x1 + (maxInLumi - x1) / 2.0; + float y2 = y1 + (maxOutLumi - y1) * 0.75; + + // horizontal distances between the last three + // control points + float h12 = x2 - x1; + float h23 = maxInLumi - x2; + // tangents at the last three control points + float m1 = (y2 - y1) / h12; + float m3 = (maxOutLumi - y2) / h23; + float m2 = (m1 + m3) / 2.0; + + if (nits < x0) { + // scale [0.0, x0] to [0.0, y0] linearly + float slope = y0 / x0; + return nits * slope; + } else if (nits < x1) { + // scale [x0, x1] to [y0, y1] linearly + float slope = (y1 - y0) / (x1 - x0); + nits = y0 + (nits - x0) * slope; + } else if (nits < x2) { + // scale [x1, x2] to [y1, y2] using Hermite interp + float t = (nits - x1) / h12; + nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * + (1.0 - t) * (1.0 - t) + + (y2 * (3.0 - 2.0 * t) + + h12 * m2 * (t - 1.0)) * t * t; + } else { + // scale [x2, maxInLumi] to [y2, maxOutLumi] using + // Hermite interp + float t = (nits - x2) / h23; + nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * + (1.0 - t) * (1.0 - t) + (maxOutLumi * + (3.0 - 2.0 * t) + h23 * m3 * + (t - 1.0)) * t * t; + } + } + + return nits; + } + )"); + break; + } + break; + default: + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: + // Map from SDR onto an HDR output buffer + // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto + // [0, maxOutLumi] which is hard-coded to be 3000 nits. + program.append(R"( + float libtonemap_ToneMapTargetNits(vec3 xyz) { + const float maxOutLumi = 3000.0; + + const float x0 = 5.0; + const float y0 = 2.5; + float x1 = in_libtonemap_displayMaxLuminance * 0.7; + float y1 = maxOutLumi * 0.15; + float x2 = in_libtonemap_displayMaxLuminance * 0.9; + float y2 = maxOutLumi * 0.45; + float x3 = in_libtonemap_displayMaxLuminance; + float y3 = maxOutLumi; + + float c1 = y1 / 3.0; + float c2 = y2 / 2.0; + float c3 = y3 / 1.5; + + float nits = xyz.y; + + if (nits <= x0) { + // scale [0.0, x0] to [0.0, y0] linearly + float slope = y0 / x0; + return nits * slope; + } else if (nits <= x1) { + // scale [x0, x1] to [y0, y1] using a curve + float t = (nits - x0) / (x1 - x0); + nits = (1.0 - t) * (1.0 - t) * y0 + + 2.0 * (1.0 - t) * t * c1 + t * t * y1; + } else if (nits <= x2) { + // scale [x1, x2] to [y1, y2] using a curve + float t = (nits - x1) / (x2 - x1); + nits = (1.0 - t) * (1.0 - t) * y1 + + 2.0 * (1.0 - t) * t * c2 + t * t * y2; + } else { + // scale [x2, x3] to [y2, y3] using a curve + float t = (nits - x2) / (x3 - x2); + nits = (1.0 - t) * (1.0 - t) * y2 + + 2.0 * (1.0 - t) * t * c3 + t * t * y3; + } + + return nits; + } + )"); + break; + default: + // For completeness, this is tone-mapping from SDR to SDR, where this is + // just a no-op. + program.append(R"( + float libtonemap_ToneMapTargetNits(vec3 xyz) { + return xyz.y; + } + )"); + break; + } + break; + } + + program.append(R"( + float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) { + if (xyz.y <= 0.0) { + return 1.0; + } + return libtonemap_ToneMapTargetNits(xyz) / xyz.y; + } + )"); + return program; + } + + std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override { + std::vector<ShaderUniform> uniforms; + + uniforms.reserve(2); + + uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance", + .value = buildUniformValue<float>(metadata.displayMaxLuminance)}); + uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance", + .value = buildUniformValue<float>(metadata.contentMaxLuminance)}); + return uniforms; + } +}; + +class ToneMapper13 : public ToneMapper { +public: + std::string generateTonemapGainShaderSkSL( + aidl::android::hardware::graphics::common::Dataspace sourceDataspace, + aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override { + const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); + const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace); + + std::string program; + // Input uniforms + program.append(R"( + uniform float in_libtonemap_displayMaxLuminance; + uniform float in_libtonemap_inputMaxLuminance; + )"); + switch (sourceDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB; + } + )"); + break; + case kTransferHLG: + // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so + // we'll clamp the luminance range in case we're mapping from PQ input to + // HLG output. + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return clamp(maxRGB, 0.0, 1000.0); + } + )"); + break; + + default: + switch (sourceDataspaceInt & kTransferMask) { + case kTransferST2084: + program.append(R"( + float libtonemap_OETFTone(float channel) { + channel = channel / 10000.0; + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float tmp = pow(channel, float(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, float(m2)); + } + )"); + break; + case kTransferHLG: + program.append(R"( + float libtonemap_OETFTone(float channel) { + channel = channel / 1000.0; + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : + a * log(12.0 * channel - b) + c; + } + )"); + break; + } + // Here we're mapping from HDR to SDR content, so interpolate using a + // Hermitian polynomial onto the smaller luminance range. + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + float maxInLumi = in_libtonemap_inputMaxLuminance; + float maxOutLumi = in_libtonemap_displayMaxLuminance; + + float nits = maxRGB; + + float x1 = maxOutLumi * 0.65; + float y1 = x1; + + float x3 = maxInLumi; + float y3 = maxOutLumi; + + float x2 = x1 + (x3 - x1) * 4.0 / 17.0; + float y2 = maxOutLumi * 0.9; + + float greyNorm1 = libtonemap_OETFTone(x1); + float greyNorm2 = libtonemap_OETFTone(x2); + float greyNorm3 = libtonemap_OETFTone(x3); + + float slope1 = 0; + float slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); + float slope3 = (y3 - y2 ) / (greyNorm3 - greyNorm2); + + if (nits < x1) { + return nits; + } + + if (nits > maxInLumi) { + return maxOutLumi; + } + + float greyNits = libtonemap_OETFTone(nits); + + if (greyNits <= greyNorm2) { + nits = (greyNits - greyNorm2) * slope2 + y2; + } else if (greyNits <= greyNorm3) { + nits = (greyNits - greyNorm3) * slope3 + y3; + } else { + nits = maxOutLumi; + } + + return nits; + } + )"); + break; + } + break; + default: + // Inverse tone-mapping and SDR-SDR mapping is not supported. + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB; + } + )"); + break; + } + + program.append(R"( + float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) { + float maxRGB = max(linearRGB.r, max(linearRGB.g, linearRGB.b)); + if (maxRGB <= 0.0) { + return 1.0; + } + return libtonemap_ToneMapTargetNits(maxRGB) / maxRGB; + } + )"); + return program; + } + + std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override { + // Hardcode the max content luminance to a "reasonable" level + static const constexpr float kContentMaxLuminance = 4000.f; + std::vector<ShaderUniform> uniforms; + uniforms.reserve(2); + uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance", + .value = buildUniformValue<float>(metadata.displayMaxLuminance)}); + uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance", + .value = buildUniformValue<float>(kContentMaxLuminance)}); + return uniforms; + } +}; + +} // namespace + +ToneMapper* getToneMapper() { + static std::once_flag sOnce; + static std::unique_ptr<ToneMapper> sToneMapper; + + std::call_once(sOnce, [&] { + switch (kToneMapAlgorithm) { + case ToneMapAlgorithm::AndroidO: + sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapperO()); + break; + case ToneMapAlgorithm::Android13: + sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapper13()); + } + }); + + return sToneMapper.get(); +} + +} // namespace android::tonemap
\ No newline at end of file diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h index 7634007771..9aae145c04 100644 --- a/libs/ui/include/ui/Fence.h +++ b/libs/ui/include/ui/Fence.h @@ -124,7 +124,7 @@ public: // getStatus() returns whether the fence has signaled yet. Prefer this to // getSignalTime() or wait() if all you care about is whether the fence has // signaled. - inline Status getStatus() { + virtual inline Status getStatus() { // The sync_wait call underlying wait() has been measured to be // significantly faster than the sync_fence_info call underlying // getSignalTime(), which might otherwise appear to be the more obvious diff --git a/libs/ui/include_mock/ui/MockFence.h b/libs/ui/include_mock/ui/MockFence.h index 162ec02455..71adee4fbc 100644 --- a/libs/ui/include_mock/ui/MockFence.h +++ b/libs/ui/include_mock/ui/MockFence.h @@ -27,6 +27,7 @@ public: virtual ~MockFence() = default; MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override)); + MOCK_METHOD(Status, getStatus, (), (override)); }; }; // namespace android::mock diff --git a/libs/ui/tests/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp index 6e520b1aee..40dddc3cf2 100644 --- a/libs/ui/tests/MockFence_test.cpp +++ b/libs/ui/tests/MockFence_test.cpp @@ -42,4 +42,16 @@ TEST_F(MockFenceTest, getSignalTime) { EXPECT_EQ(1234, fence->getSignalTime()); } +TEST_F(MockFenceTest, getStatus) { + sp<Fence> fence = getFenceForTesting(); + + EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Unsignaled)); + EXPECT_EQ(Fence::Status::Unsignaled, fence->getStatus()); + + EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Signaled)); + EXPECT_EQ(Fence::Status::Signaled, fence->getStatus()); + + EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Invalid)); + EXPECT_EQ(Fence::Status::Invalid, fence->getStatus()); +} } // namespace android::ui diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 73e57495e6..8cdb706bcc 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -73,7 +73,6 @@ cc_defaults { "libui", "lib-platform-compat-native-api", "server_configurable_flags", - "InputFlingerProperties", ], static_libs: [ "libattestation", @@ -125,7 +124,7 @@ filegroup { "InputListener.cpp", "InputReaderBase.cpp", "InputThread.cpp", - "VibrationElement.cpp" + "VibrationElement.cpp", ], } diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 171f2b5ce8..4757d31d91 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -68,7 +68,6 @@ cc_defaults { "libutils", "lib-platform-compat-native-api", "server_configurable_flags", - "InputFlingerProperties", ], static_libs: [ "libattestation", diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 176cf8943a..5c56e842db 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -19,7 +19,6 @@ #define LOG_NDEBUG 1 -#include <InputFlingerProperties.sysprop.h> #include <android-base/chrono_utils.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -112,15 +111,6 @@ private: std::mutex& mMutex; }; -// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display -// coordinates and SurfaceFlinger includes the display rotation in the input window transforms. -bool isPerWindowInputRotationEnabled() { - static const bool PER_WINDOW_INPUT_ROTATION = - sysprop::InputFlingerProperties::per_window_input_rotation().value_or(true); - - return PER_WINDOW_INPUT_ROTATION; -} - // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::milliseconds( @@ -342,18 +332,6 @@ bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, std::shared_ptr<EventEntry> eventEntry, int32_t inputTargetFlags) { - if (eventEntry->type == EventEntry::Type::MOTION) { - const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); - if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) || - (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) { - const ui::Transform identityTransform; - // 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, - identityTransform, 1.0f /*globalScaleFactor*/); - } - } - if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, @@ -2531,8 +2509,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa if (displayInfoIt != mDisplayInfos.end()) { inputTarget.displayTransform = displayInfoIt->second.transform; } else { - ALOGI_IF(isPerWindowInputRotationEnabled(), - "DisplayInfo not found for window on display: %d", windowInfo->displayId); + ALOGE("DisplayInfo not found for window on display: %d", windowInfo->displayId); } inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; @@ -3257,8 +3234,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry); status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, focusEntry.id, - focusEntry.hasFocus, - mInTouchMode); + focusEntry.hasFocus); break; } @@ -4737,21 +4713,19 @@ 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<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) { - const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle); - if (newWindowHandle != nullptr && - newWindowHandle->getInfo()->transform.getOrientation() != - oldWindowOrientations[oldWindowHandle->getId()]) { - std::shared_ptr<InputChannel> inputChannel = - getInputChannelLocked(newWindowHandle->getToken()); - if (inputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "touched window's orientation changed"); - synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); - } + // Determine if the orientation of any of the input windows have changed, and cancel all + // pointer events if necessary. + for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) { + const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle); + if (newWindowHandle != nullptr && + newWindowHandle->getInfo()->transform.getOrientation() != + oldWindowOrientations[oldWindowHandle->getId()]) { + std::shared_ptr<InputChannel> inputChannel = + getInputChannelLocked(newWindowHandle->getToken()); + if (inputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "touched window's orientation changed"); + synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); } } } diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index ee7b392bcd..51546ce983 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -71,7 +71,6 @@ cc_defaults { "libstatslog", "libui", "libutils", - "InputFlingerProperties", ], static_libs: [ "libc++fs", @@ -86,7 +85,7 @@ cc_library_shared { name: "libinputreader", defaults: [ "inputflinger_defaults", - "libinputreader_defaults" + "libinputreader_defaults", ], srcs: [ "InputReaderFactory.cpp", @@ -100,6 +99,6 @@ cc_library_shared { "libinputreader_headers", ], static_libs: [ - "libc++fs" + "libc++fs", ], } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index f3d7cdc48b..15ba45945a 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -193,28 +193,18 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); - if (isPerWindowInputRotationEnabled()) { - // When per-window input rotation is enabled, InputReader works in the un-rotated - // coordinate space, so we don't need to do anything if the device is already - // orientation-aware. If the device is not orientation-aware, then we need to apply the - // inverse rotation of the display so that when the display rotation is applied later - // as a part of the per-window transform, we get the expected screen coordinates. - if (!isOrientedDevice) { - std::optional<DisplayViewport> internalViewport = - config->getDisplayViewportByType(ViewportType::INTERNAL); - if (internalViewport) { - mOrientation = getInverseRotation(internalViewport->orientation); - mDisplayWidth = internalViewport->deviceWidth; - mDisplayHeight = internalViewport->deviceHeight; - } - } - } else { - if (isOrientedDevice) { - std::optional<DisplayViewport> internalViewport = - config->getDisplayViewportByType(ViewportType::INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } + // InputReader works in the un-rotated display coordinate space, so we don't need to do + // anything if the device is already orientation-aware. If the device is not + // orientation-aware, then we need to apply the inverse rotation of the display so that + // when the display rotation is applied later as a part of the per-window transform, we + // get the expected screen coordinates. + if (!isOrientedDevice) { + std::optional<DisplayViewport> internalViewport = + config->getDisplayViewportByType(ViewportType::INTERNAL); + if (internalViewport) { + mOrientation = getInverseRotation(internalViewport->orientation); + mDisplayWidth = internalViewport->deviceWidth; + mDisplayHeight = internalViewport->deviceHeight; } } @@ -347,12 +337,11 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { if (moved) { float dx = deltaX; float dy = deltaY; - if (isPerWindowInputRotationEnabled()) { - // Rotate the delta from InputReader's un-rotated coordinate space to - // PointerController's rotated coordinate space that is oriented with the - // viewport. - rotateDelta(getInverseRotation(mOrientation), &dx, &dy); - } + // Rotate the delta from InputReader's un-rotated coordinate space to + // PointerController's rotated coordinate space that is oriented with the + // viewport. + rotateDelta(getInverseRotation(mOrientation), &dx, &dy); + mPointerController->move(dx, dy); } @@ -364,12 +353,11 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - if (isPerWindowInputRotationEnabled()) { - // Rotate the cursor position that is in PointerController's rotated coordinate space - // to InputReader's un-rotated coordinate space. - rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/, - mDisplayWidth, mDisplayHeight); - } + // Rotate the cursor position that is in PointerController's rotated coordinate space + // to InputReader's un-rotated coordinate space. + rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/, + mDisplayWidth, mDisplayHeight); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 197be98172..8c30e38908 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -17,7 +17,6 @@ #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H -#include <InputFlingerProperties.sysprop.h> #include <input/DisplayViewport.h> #include <stdint.h> @@ -29,13 +28,6 @@ namespace android { // --- Static Definitions --- -// When per-window input rotation is enabled, display transformations such as rotation and -// projection are part of the input window's transform. This means InputReader should work in the -// un-rotated coordinate space. -static bool isPerWindowInputRotationEnabled() { - return sysprop::InputFlingerProperties::per_window_input_rotation().value_or(true); -} - static int32_t getInverseRotation(int32_t orientation) { switch (orientation) { case DISPLAY_ORIENTATION_90: diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index fd33df9347..3fe6fd130f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -168,17 +168,13 @@ TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext), mSource(0), mDeviceMode(DeviceMode::DISABLED), - mRawSurfaceWidth(-1), - mRawSurfaceHeight(-1), - mSurfaceLeft(0), - mSurfaceTop(0), - mSurfaceRight(0), - mSurfaceBottom(0), + mDisplayWidth(-1), + mDisplayHeight(-1), mPhysicalWidth(-1), mPhysicalHeight(-1), mPhysicalLeft(0), mPhysicalTop(0), - mSurfaceOrientation(DISPLAY_ORIENTATION_0) {} + mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {} TouchInputMapper::~TouchInputMapper() {} @@ -266,11 +262,9 @@ void TouchInputMapper::dump(std::string& dump) { dumpRawPointerAxes(dump); dumpCalibration(dump); dumpAffineTransformation(dump); - dumpSurface(dump); + dumpDisplay(dump); dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); - dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); - dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); @@ -390,9 +384,9 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | InputReaderConfiguration::CHANGE_SHOW_TOUCHES | InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { - // Configure device sources, surface dimensions, orientation and + // Configure device sources, display dimensions, orientation and // scaling factors. - configureSurface(when, &resetNeeded); + configureInputDevice(when, &resetNeeded); } if (changes && resetNeeded) { @@ -615,7 +609,7 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() { return std::make_optional(newViewport); } -void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { +void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) { DeviceMode oldDeviceMode = mDeviceMode; resolveExternalStylusPresence(); @@ -673,31 +667,28 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { } // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); + const int32_t rawWidth = mRawPointerAxes.getRawWidth(); + const int32_t rawHeight = mRawPointerAxes.getRawHeight(); - bool viewportChanged = mViewport != *newViewport; + const bool viewportChanged = mViewport != *newViewport; bool skipViewportUpdate = false; if (viewportChanged) { - bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation; + const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation; mViewport = *newViewport; if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) { - // Convert rotated viewport to natural surface coordinates. - int32_t naturalLogicalWidth, naturalLogicalHeight; + // Convert rotated viewport to the natural orientation. int32_t naturalPhysicalWidth, naturalPhysicalHeight; int32_t naturalPhysicalLeft, naturalPhysicalTop; int32_t naturalDeviceWidth, naturalDeviceHeight; - // 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 = + // Apply the inverse of the input device orientation so that the input device is + // configured in the same orientation as the viewport. The input device orientation will + // be re-applied by mInputDeviceOrientation. + const int32_t naturalDeviceOrientation = (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4; - switch (naturalSurfaceOrientation) { + switch (naturalDeviceOrientation) { case DISPLAY_ORIENTATION_90: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; @@ -706,8 +697,6 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { naturalDeviceHeight = mViewport.deviceWidth; break; case DISPLAY_ORIENTATION_180: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; @@ -716,8 +705,6 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { naturalDeviceHeight = mViewport.deviceHeight; break; case DISPLAY_ORIENTATION_270: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; naturalPhysicalLeft = mViewport.physicalTop; @@ -727,8 +714,6 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { break; case DISPLAY_ORIENTATION_0: default: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalLeft = mViewport.physicalLeft; @@ -749,58 +734,36 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPhysicalLeft = naturalPhysicalLeft; mPhysicalTop = naturalPhysicalTop; - if (isPerWindowInputRotationEnabled()) { - // When per-window input rotation is enabled, InputReader works in the display - // space, so the surface bounds are the bounds of the display device. - const int32_t oldSurfaceWidth = mRawSurfaceWidth; - const int32_t oldSurfaceHeight = mRawSurfaceHeight; - mRawSurfaceWidth = naturalDeviceWidth; - mRawSurfaceHeight = naturalDeviceHeight; - mSurfaceLeft = 0; - mSurfaceTop = 0; - mSurfaceRight = mRawSurfaceWidth; - mSurfaceBottom = mRawSurfaceHeight; - // When per-window input rotation is enabled, InputReader works in the un-rotated - // coordinate space, so we don't need to do anything if the device is already - // orientation-aware. If the device is not orientation-aware, then we need to apply - // the inverse rotation of the display so that when the display rotation is applied - // later as a part of the per-window transform, we get the expected screen - // coordinates. - mSurfaceOrientation = mParameters.orientationAware - ? DISPLAY_ORIENTATION_0 - : getInverseRotation(mViewport.orientation); - // For orientation-aware devices that work in the un-rotated coordinate space, the - // viewport update should be skipped if it is only a change in the orientation. - skipViewportUpdate = mParameters.orientationAware && - mRawSurfaceWidth == oldSurfaceWidth && - mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged; - } else { - mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; - mRawSurfaceHeight = - naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; - mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; - mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; - mSurfaceRight = mSurfaceLeft + naturalLogicalWidth; - mSurfaceBottom = mSurfaceTop + naturalLogicalHeight; - - mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation - : DISPLAY_ORIENTATION_0; - } + const int32_t oldDisplayWidth = mDisplayWidth; + const int32_t oldDisplayHeight = mDisplayHeight; + mDisplayWidth = naturalDeviceWidth; + mDisplayHeight = naturalDeviceHeight; + + // InputReader works in the un-rotated display coordinate space, so we don't need to do + // anything if the device is already orientation-aware. If the device is not + // orientation-aware, then we need to apply the inverse rotation of the display so that + // when the display rotation is applied later as a part of the per-window transform, we + // get the expected screen coordinates. + mInputDeviceOrientation = mParameters.orientationAware + ? DISPLAY_ORIENTATION_0 + : getInverseRotation(mViewport.orientation); + // For orientation-aware devices that work in the un-rotated coordinate space, the + // viewport update should be skipped if it is only a change in the orientation. + skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth && + mDisplayHeight == oldDisplayHeight && viewportOrientationChanged; // Apply the input device orientation for the device. - mSurfaceOrientation = - (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4; + mInputDeviceOrientation = + (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4; } else { mPhysicalWidth = rawWidth; mPhysicalHeight = rawHeight; mPhysicalLeft = 0; mPhysicalTop = 0; - mRawSurfaceWidth = rawWidth; - mRawSurfaceHeight = rawHeight; - mSurfaceLeft = 0; - mSurfaceTop = 0; - mSurfaceOrientation = DISPLAY_ORIENTATION_0; + mDisplayWidth = rawWidth; + mDisplayHeight = rawHeight; + mInputDeviceOrientation = DISPLAY_ORIENTATION_0; } } @@ -829,14 +792,12 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) { ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " "display id %d", - getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight, - mSurfaceOrientation, mDeviceMode, mViewport.displayId); + getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight, + mInputDeviceOrientation, mDeviceMode, mViewport.displayId); // Configure X and Y factors. - mXScale = float(mRawSurfaceWidth) / rawWidth; - mYScale = float(mRawSurfaceHeight) / rawHeight; - mXTranslate = -mSurfaceLeft; - mYTranslate = -mSurfaceTop; + mXScale = float(mDisplayWidth) / rawWidth; + mYScale = float(mDisplayHeight) / rawHeight; mXPrecision = 1.0f / mXScale; mYPrecision = 1.0f / mYScale; @@ -853,7 +814,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mGeometricScale = avg(mXScale, mYScale); // Size of diagonal axis. - float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight); + float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight); // Size factors. if (mCalibration.sizeCalibration != Calibration::SizeCalibration::NONE) { @@ -1015,21 +976,21 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // Compute oriented precision, scales and ranges. // Note that the maximum value reported is an inclusive maximum value so it is one - // unit less than the total width or height of surface. - switch (mSurfaceOrientation) { + // unit less than the total width or height of the display. + switch (mInputDeviceOrientation) { case DISPLAY_ORIENTATION_90: case DISPLAY_ORIENTATION_270: mOrientedXPrecision = mYPrecision; mOrientedYPrecision = mXPrecision; - mOrientedRanges.x.min = mYTranslate; - mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1; + mOrientedRanges.x.min = 0; + mOrientedRanges.x.max = mDisplayHeight - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; - mOrientedRanges.y.min = mXTranslate; - mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1; + mOrientedRanges.y.min = 0; + mOrientedRanges.y.max = mDisplayWidth - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; @@ -1039,14 +1000,14 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedXPrecision = mXPrecision; mOrientedYPrecision = mYPrecision; - mOrientedRanges.x.min = mXTranslate; - mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1; + mOrientedRanges.x.min = 0; + mOrientedRanges.x.max = mDisplayWidth - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; - mOrientedRanges.y.min = mYTranslate; - mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1; + mOrientedRanges.y.min = 0; + mOrientedRanges.y.max = mDisplayHeight - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; @@ -1059,7 +1020,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { if (mDeviceMode == DeviceMode::POINTER) { // Compute pointer gesture detection parameters. float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight); + float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight); // Scale movements such that one whole swipe of the touch pad covers a // given area relative to the diagonal size of the display when no acceleration @@ -1093,19 +1054,15 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { } } -void TouchInputMapper::dumpSurface(std::string& dump) { +void TouchInputMapper::dumpDisplay(std::string& dump) { dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth); - dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight); - dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); - dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); - dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight); - dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom); + dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth); + dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight); dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); - dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); + dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation); } void TouchInputMapper::configureVirtualKeys() { @@ -1144,16 +1101,16 @@ void TouchInputMapper::configureVirtualKeys() { int32_t halfHeight = virtualKeyDefinition.height / 2; virtualKey.hitLeft = - (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth + + (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth + touchScreenLeft; virtualKey.hitRight = - (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth + + (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth + touchScreenLeft; - virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / - mRawSurfaceHeight + + virtualKey.hitTop = + (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight + touchScreenTop; - virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / - mRawSurfaceHeight + + virtualKey.hitBottom = + (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight + touchScreenTop; mVirtualKeys.push_back(virtualKey); } @@ -1419,7 +1376,7 @@ void TouchInputMapper::dumpAffineTransformation(std::string& dump) { void TouchInputMapper::updateAffineTransformation() { mAffineTransform = getPolicy()->getTouchAffineTransformation(getDeviceContext().getDescriptor(), - mSurfaceOrientation); + mInputDeviceOrientation); } void TouchInputMapper::reset(nsecs_t when) { @@ -1867,8 +1824,10 @@ bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_ // Pointer just went down. Check for virtual key press or off-screen touches. uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - // Exclude unscaled device for inside surface checking. - if (!isPointInsideSurface(pointer.x, pointer.y) && mDeviceMode != DeviceMode::UNSCALED) { + // Skip checking whether the pointer is inside the physical frame if the device is in + // unscaled mode. + if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) && + mDeviceMode != DeviceMode::UNSCALED) { // If exactly one pointer went down, check for virtual key hit. // Otherwise we will drop the entire stroke. if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { @@ -2137,7 +2096,7 @@ void TouchInputMapper::cookPointerData() { } // Walk through the the active pointers and map device coordinates onto - // surface coordinates and adjust for display orientation. + // display coordinates and adjust for display orientation. for (uint32_t i = 0; i < currentPointerCount; i++) { const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; @@ -2297,15 +2256,15 @@ void TouchInputMapper::cookPointerData() { mAffineTransform.applyTo(xTransformed, yTransformed); rotateAndScale(xTransformed, yTransformed); - // Adjust X, Y, and coverage coords for surface orientation. + // Adjust X, Y, and coverage coords for input device orientation. float left, top, right, bottom; - switch (mSurfaceOrientation) { + switch (mInputDeviceOrientation) { case DISPLAY_ORIENTATION_90: - left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; - top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; + left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale; + right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale; + bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; + top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; orientation -= M_PI_2; if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) { @@ -2316,8 +2275,8 @@ void TouchInputMapper::cookPointerData() { case DISPLAY_ORIENTATION_180: left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; - bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; - top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; + bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; + top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; orientation -= M_PI; if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) { @@ -2328,8 +2287,8 @@ void TouchInputMapper::cookPointerData() { case DISPLAY_ORIENTATION_270: left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; - bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale; + top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale; orientation += M_PI_2; if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) { @@ -2338,10 +2297,10 @@ void TouchInputMapper::cookPointerData() { } break; default: - left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale; + right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale; + bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale; + top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale; break; } @@ -2854,7 +2813,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerVelocityControl.move(when, &deltaX, &deltaY); // Move the pointer using a relative motion. @@ -2988,7 +2947,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerVelocityControl.move(when, &deltaX, &deltaY); // Move the pointer using a relative motion. @@ -3246,7 +3205,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi commonDeltaX *= mPointerXMovementScale; commonDeltaY *= mPointerYMovementScale; - rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); + rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY); mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); mPointerGesture.referenceGestureX += commonDeltaX; @@ -3356,7 +3315,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mCurrentRawState.rawPointerData.pointerForId(touchId); float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerGesture.currentGestureProperties[i].clear(); mPointerGesture.currentGestureProperties[i].id = gestureId; @@ -3472,7 +3431,7 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint mLastRawState.rawPointerData.pointers[lastIndex].y) * mPointerYMovementScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerVelocityControl.move(when, &deltaX, &deltaY); moveMouseCursor(deltaX, deltaY); @@ -3697,7 +3656,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p const int32_t deviceId = getDeviceId(); std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames(); std::for_each(frames.begin(), frames.end(), - [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); + [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); }); NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId, policyFlags, action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, @@ -3741,56 +3700,49 @@ void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) { abortTouches(when, readTime, 0 /* policyFlags*/); } -// Transform raw coordinate to surface coordinate -void TouchInputMapper::rotateAndScale(float& x, float& y) { - // Scale to surface coordinate. +// Transform input device coordinates to display panel coordinates. +void TouchInputMapper::rotateAndScale(float& x, float& y) const { const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale; const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale; const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale; const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale; - // Rotate to surface coordinate. + // Rotate to display coordinate. // 0 - no swap and reverse. // 90 - swap x/y and reverse y. // 180 - reverse x, y. // 270 - swap x/y and reverse x. - switch (mSurfaceOrientation) { + switch (mInputDeviceOrientation) { case DISPLAY_ORIENTATION_0: - x = xScaled + mXTranslate; - y = yScaled + mYTranslate; + x = xScaled; + y = yScaled; break; case DISPLAY_ORIENTATION_90: - y = xScaledMax - (mRawSurfaceWidth - mSurfaceRight); - x = yScaled + mYTranslate; + y = xScaledMax; + x = yScaled; break; case DISPLAY_ORIENTATION_180: - x = xScaledMax - (mRawSurfaceWidth - mSurfaceRight); - y = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom); + x = xScaledMax; + y = yScaledMax; break; case DISPLAY_ORIENTATION_270: - y = xScaled + mXTranslate; - x = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom); + y = xScaled; + x = yScaledMax; break; default: assert(false); } } -bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { +bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const { const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale; const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale; - if (isPerWindowInputRotationEnabled()) { - return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) && - y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight); - } return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight && + xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom; + yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight); } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { @@ -4048,11 +4000,9 @@ std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() { } void TouchInputMapper::moveMouseCursor(float dx, float dy) const { - if (isPerWindowInputRotationEnabled()) { - // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate - // space that is oriented with the viewport. - rotateDelta(mViewport.orientation, &dx, &dy); - } + // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate + // space that is oriented with the viewport. + rotateDelta(mViewport.orientation, &dx, &dy); mPointerController->move(dx, dy); } @@ -4062,7 +4012,6 @@ std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const { float y = 0; mPointerController->getPosition(&x, &y); - if (!isPerWindowInputRotationEnabled()) return {x, y}; if (!mViewport.isValid()) return {x, y}; // Convert from PointerController's rotated coordinate space that is oriented with the viewport @@ -4073,11 +4022,9 @@ std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const { } void TouchInputMapper::setMouseCursorPosition(float x, float y) const { - if (isPerWindowInputRotationEnabled() && mViewport.isValid()) { - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space that is oriented with the viewport. - rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight); - } + // Convert from InputReader's un-rotated coordinate space to PointerController's rotated + // coordinate space that is oriented with the viewport. + rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); mPointerController->setPosition(x, y); } @@ -4092,11 +4039,9 @@ void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint float y = spotCoords[index].getY(); float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - if (isPerWindowInputRotationEnabled()) { - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space. - rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight); - } + // Convert from InputReader's un-rotated coordinate space to PointerController's rotated + // coordinate space. + rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x); outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 3340672021..496491b62d 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -406,8 +406,8 @@ protected: virtual void dumpParameters(std::string& dump); virtual void configureRawPointerAxes(); virtual void dumpRawPointerAxes(std::string& dump); - virtual void configureSurface(nsecs_t when, bool* outResetNeeded); - virtual void dumpSurface(std::string& dump); + virtual void configureInputDevice(nsecs_t when, bool* outResetNeeded); + virtual void dumpDisplay(std::string& dump); virtual void configureVirtualKeys(); virtual void dumpVirtualKeys(std::string& dump); virtual void parseCalibration(); @@ -426,39 +426,27 @@ private: // The components of the viewport are specified in the display's rotated orientation. DisplayViewport mViewport; - // The surface orientation, width and height set by configureSurface(). - // The width and height are derived from the viewport but are specified + // The width and height are obtained from the viewport and are specified // in the natural orientation. - // They could be used for calculating diagonal, scaling factors, and virtual keys. - int32_t mRawSurfaceWidth; - int32_t mRawSurfaceHeight; - - // The surface origin specifies how the surface coordinates should be translated - // to align with the logical display coordinate space. - // TODO(b/188939842): Remove surface coordinates when Per-Window Input Rotation is enabled. - int32_t mSurfaceLeft; - int32_t mSurfaceTop; - int32_t mSurfaceRight; - int32_t mSurfaceBottom; - - // Similar to the surface coordinates, but in the raw display coordinate space rather than in - // the logical coordinate space. + int32_t mDisplayWidth; + int32_t mDisplayHeight; + + // The physical frame is the rectangle in the display's coordinate space that maps to the + // the logical display frame. int32_t mPhysicalWidth; int32_t mPhysicalHeight; int32_t mPhysicalLeft; int32_t mPhysicalTop; - // The orientation may be different from the viewport orientation as it specifies - // the rotation of the surface coordinates required to produce the viewport's - // requested orientation, so it will depend on whether the device is orientation aware. - int32_t mSurfaceOrientation; + // The orientation of the input device relative to that of the display panel. It specifies + // the rotation of the input device coordinates required to produce the display panel + // orientation, so it will depend on whether the device is orientation aware. + int32_t mInputDeviceOrientation; // Translation and scaling factors, orientation-independent. - float mXTranslate; float mXScale; float mXPrecision; - float mYTranslate; float mYScale; float mYPrecision; @@ -808,13 +796,13 @@ private: // touchscreen. void updateTouchSpots(); - bool isPointInsideSurface(int32_t x, int32_t y); + bool isPointInsidePhysicalFrame(int32_t x, int32_t y) const; const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); static void assignPointerIds(const RawState& last, RawState& current); const char* modeToString(DeviceMode deviceMode); - void rotateAndScale(float& x, float& y); + void rotateAndScale(float& x, float& y) const; // Wrapper methods for interfacing with PointerController. These are used to convert points // between the coordinate spaces used by InputReader and PointerController, if they differ. diff --git a/services/inputflinger/sysprop/Android.bp b/services/inputflinger/sysprop/Android.bp deleted file mode 100644 index b9d65ee246..0000000000 --- a/services/inputflinger/sysprop/Android.bp +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 1c7e724332..0000000000 --- a/services/inputflinger/sysprop/InputFlingerProperties.sysprop +++ /dev/null @@ -1,27 +0,0 @@ -# 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/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index ba0ce95458..515a01e137 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -842,7 +842,6 @@ public: FocusEvent* focusEvent = static_cast<FocusEvent*>(event); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); - EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); } void consumeCaptureEvent(bool hasCapture) { @@ -3012,59 +3011,6 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState); } -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); - const std::string name = window->getName(); - - // Window gets transformed by offset values. - window->setWindowOffset(500.0f, 500.0f); - - mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocusable(true); - - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - - // First, we set focused window so that focusedWindowHandle is not null. - setFocusedWindow(window); - - // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked. - window->consumeFocusEvent(true); - - 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(action, motionEvent.getAction()); - EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount()); - - float expectedX = motionArgs.pointerCoords[0].getX(); - float expectedY = motionArgs.pointerCoords[0].getY(); - - // Ensure the axis values from the final motion event are not transformed. - EXPECT_EQ(expectedX, motionEvent.getX(0)) - << "expected " << expectedX << " for x coord of " << name.c_str() << ", got " - << motionEvent.getX(0); - EXPECT_EQ(expectedY, motionEvent.getY(0)) - << "expected " << expectedY << " for y coord of " << name.c_str() << ", got " - << motionEvent.getY(0); - // Ensure the raw and transformed axis values for the motion event are the same. - EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0)) - << "expected raw and transformed X-axis values to be equal"; - EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0)) - << "expected raw and transformed Y-axis values to be equal"; - } -} - /** * Ensure that separate calls to sign the same data are generating the same key. * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 53b03ada3a..336afc67fc 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -16,7 +16,6 @@ #include <CursorInputMapper.h> #include <InputDevice.h> -#include <InputFlingerProperties.sysprop.h> #include <InputMapper.h> #include <InputReader.h> #include <InputReaderBase.h> @@ -2695,7 +2694,6 @@ 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; @@ -2713,18 +2711,12 @@ protected: } void SetUp() override { - // Ensure per_window_input_rotation is enabled. - sysprop::InputFlingerProperties::per_window_input_rotation(true); - SetUp(DEVICE_CLASSES); } void TearDown() override { mFakeListener.reset(); mFakePolicy.clear(); - - sysprop::InputFlingerProperties::per_window_input_rotation( - INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE); } void addConfigurationProperty(const char* key, const char* value) { @@ -2836,8 +2828,6 @@ 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 --- diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index db1a1cc29f..c67acbfbcd 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -23,31 +23,32 @@ #include <android-base/logging.h> #include <android/util/ProtoOutputStream.h> +#include <cutils/atomic.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <sensors/convert.h> -#include <cutils/atomic.h> #include <utils/Errors.h> #include <utils/Singleton.h> -#include <cstddef> #include <chrono> #include <cinttypes> +#include <cstddef> #include <thread> using namespace android::hardware::sensors; using namespace android::hardware::sensors::V1_0; using namespace android::hardware::sensors::V1_0::implementation; +using android::hardware::hidl_vec; +using android::hardware::Return; using android::hardware::sensors::V2_0::EventQueueFlagBits; using android::hardware::sensors::V2_0::WakeLockQueueFlagBits; using android::hardware::sensors::V2_1::ISensorsCallback; -using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo; -using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos; using android::hardware::sensors::V2_1::implementation::convertToNewEvents; +using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos; +using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo; +using android::hardware::sensors::V2_1::implementation::convertToSensor; using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0; using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0; using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1; -using android::hardware::hidl_vec; -using android::hardware::Return; using android::SensorDeviceUtils::HidlServiceRegistrationWaiter; using android::util::ProtoOutputStream; @@ -73,7 +74,7 @@ status_t statusFromResult(Result result) { } } -template<typename EnumType> +template <typename EnumType> constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) { return static_cast<typename std::underlying_type<EnumType>::type>(value); } @@ -81,14 +82,13 @@ constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType valu // Used internally by the framework to wake the Event FMQ. These values must start after // the last value of EventQueueFlagBits enum EventQueueFlagBitsInternal : uint32_t { - INTERNAL_WAKE = 1 << 16, + INTERNAL_WAKE = 1 << 16, }; -} // anonymous namespace +} // anonymous namespace void SensorsHalDeathReceivier::serviceDied( - uint64_t /* cookie */, - const wp<::android::hidl::base::V1_0::IBase>& /* service */) { + uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* service */) { ALOGW("Sensors HAL died, attempting to reconnect."); SensorDevice::getInstance().prepareForReconnect(); } @@ -98,29 +98,36 @@ struct SensorsCallback : public ISensorsCallback { using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo; Return<void> onDynamicSensorsConnected_2_1( - const hidl_vec<SensorInfo> &dynamicSensorsAdded) override { - return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded); + const hidl_vec<SensorInfo>& dynamicSensorsAdded) override { + std::vector<sensor_t> sensors; + for (const V2_1::SensorInfo& info : dynamicSensorsAdded) { + sensor_t sensor; + convertToSensor(info, &sensor); + sensors.push_back(sensor); + } + + SensorDevice::getInstance().onDynamicSensorsConnected(sensors); + return Return<void>(); } Return<void> onDynamicSensorsConnected( - const hidl_vec<V1_0::SensorInfo> &dynamicSensorsAdded) override { - return SensorDevice::getInstance().onDynamicSensorsConnected( - convertToNewSensorInfos(dynamicSensorsAdded)); + const hidl_vec<V1_0::SensorInfo>& dynamicSensorsAdded) override { + return onDynamicSensorsConnected_2_1(convertToNewSensorInfos(dynamicSensorsAdded)); } Return<void> onDynamicSensorsDisconnected( - const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override { - return SensorDevice::getInstance().onDynamicSensorsDisconnected( - dynamicSensorHandlesRemoved); + const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override { + SensorDevice::getInstance().onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved); + return Return<void>(); } }; SensorDevice::SensorDevice() - : mHidlTransportErrors(20), - mRestartWaiter(new HidlServiceRegistrationWaiter()), - mEventQueueFlag(nullptr), - mWakeLockQueueFlag(nullptr), - mReconnecting(false) { + : mHidlTransportErrors(20), + mRestartWaiter(new HidlServiceRegistrationWaiter()), + mEventQueueFlag(nullptr), + mWakeLockQueueFlag(nullptr), + mReconnecting(false) { if (!connectHidlService()) { return; } @@ -132,61 +139,59 @@ SensorDevice::SensorDevice() } void SensorDevice::initializeSensorList() { - checkReturn(mSensors->getSensorsList( - [&](const auto &list) { - const size_t count = list.size(); - - mActivationCount.setCapacity(count); - Info model; - for (size_t i=0 ; i < count; i++) { - sensor_t sensor; - convertToSensor(convertToOldSensorInfo(list[i]), &sensor); - - if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) { - sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor); - - // Some sensors don't have a default resolution and will be left at 0. - // Don't crash in this case since CTS will verify that devices don't go to - // production with a resolution of 0. - if (sensor.resolution != 0) { - float quantizedRange = sensor.maxRange; - SensorDeviceUtils::quantizeValue( - &quantizedRange, sensor.resolution, /*factor=*/ 1); - // Only rewrite maxRange if the requantization produced a "significant" - // change, which is fairly arbitrarily defined as resolution / 8. - // Smaller deltas are permitted, as they may simply be due to floating - // point representation error, etc. - if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) { - ALOGW("%s's max range %.12f is not a multiple of the resolution " - "%.12f - updated to %.12f", sensor.name, sensor.maxRange, - sensor.resolution, quantizedRange); - sensor.maxRange = quantizedRange; - } - } else { - // Don't crash here or the device will go into a crashloop. - ALOGW("%s should have a non-zero resolution", sensor.name); - } - } + checkReturn(mSensors->getSensorsList([&](const auto& list) { + const size_t count = list.size(); - // Check and clamp power if it is 0 (or close) - constexpr float MIN_POWER_MA = 0.001; // 1 microAmp - if (sensor.power < MIN_POWER_MA) { - ALOGI("%s's reported power %f invalid, clamped to %f", - sensor.name, sensor.power, MIN_POWER_MA); - sensor.power = MIN_POWER_MA; + mActivationCount.setCapacity(count); + Info model; + for (size_t i = 0; i < count; i++) { + sensor_t sensor; + convertToSensor(list[i], &sensor); + + if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) { + sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor); + + // Some sensors don't have a default resolution and will be left at 0. + // Don't crash in this case since CTS will verify that devices don't go to + // production with a resolution of 0. + if (sensor.resolution != 0) { + float quantizedRange = sensor.maxRange; + SensorDeviceUtils::quantizeValue(&quantizedRange, sensor.resolution, + /*factor=*/1); + // Only rewrite maxRange if the requantization produced a "significant" + // change, which is fairly arbitrarily defined as resolution / 8. + // Smaller deltas are permitted, as they may simply be due to floating + // point representation error, etc. + if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) { + ALOGW("%s's max range %.12f is not a multiple of the resolution " + "%.12f - updated to %.12f", + sensor.name, sensor.maxRange, sensor.resolution, quantizedRange); + sensor.maxRange = quantizedRange; } - mSensorList.push_back(sensor); + } else { + // Don't crash here or the device will go into a crashloop. + ALOGW("%s should have a non-zero resolution", sensor.name); + } + } + + // Check and clamp power if it is 0 (or close) + constexpr float MIN_POWER_MA = 0.001; // 1 microAmp + if (sensor.power < MIN_POWER_MA) { + ALOGI("%s's reported power %f invalid, clamped to %f", sensor.name, sensor.power, + MIN_POWER_MA); + sensor.power = MIN_POWER_MA; + } + mSensorList.push_back(sensor); - mActivationCount.add(list[i].sensorHandle, model); + mActivationCount.add(list[i].sensorHandle, model); - // Only disable all sensors on HAL 1.0 since HAL 2.0 - // handles this in its initialize method - if (!mSensors->supportsMessageQueues()) { - checkReturn(mSensors->activate(list[i].sensorHandle, - 0 /* enabled */)); - } - } - })); + // Only disable all sensors on HAL 1.0 since HAL 2.0 + // handles this in its initialize method + if (!mSensors->supportsMessageQueues()) { + checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */)); + } + } + })); } SensorDevice::~SensorDevice() { @@ -231,7 +236,7 @@ SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() { // Poke ISensor service. If it has lingering connection from previous generation of // system server, it will kill itself. There is no intention to handle the poll result, // which will be done since the size is 0. - if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) { + if (mSensors->poll(0, [](auto, const auto&, const auto&) {}).isOk()) { // ok to continue connectionStatus = HalConnectionStatus::CONNECTED; break; @@ -278,23 +283,23 @@ SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_1() { SensorDevice::HalConnectionStatus SensorDevice::initializeHidlServiceV2_X() { HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN; - mWakeLockQueue = std::make_unique<WakeLockQueue>( - SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT, - true /* configureEventFlagWord */); + mWakeLockQueue = + std::make_unique<WakeLockQueue>(SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT, + true /* configureEventFlagWord */); hardware::EventFlag::deleteEventFlag(&mEventQueueFlag); - hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag); + hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), + &mEventQueueFlag); hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag); - hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), - &mWakeLockQueueFlag); + hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakeLockQueueFlag); - CHECK(mSensors != nullptr && mWakeLockQueue != nullptr && - mEventQueueFlag != nullptr && mWakeLockQueueFlag != nullptr); + CHECK(mSensors != nullptr && mWakeLockQueue != nullptr && mEventQueueFlag != nullptr && + mWakeLockQueueFlag != nullptr); - status_t status = checkReturnAndGetStatus(mSensors->initialize( - *mWakeLockQueue->getDesc(), - new SensorsCallback())); + mCallback = new SensorsCallback(); + status_t status = + checkReturnAndGetStatus(mSensors->initialize(*mWakeLockQueue->getDesc(), mCallback)); if (status != NO_ERROR) { connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT; @@ -326,7 +331,7 @@ void SensorDevice::reconnect() { mActivationCount.clear(); mSensorList.clear(); - if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) { + if (connectHidlService()) { initializeSensorList(); if (sensorHandlesChanged(previousSensorList, mSensorList)) { @@ -338,8 +343,8 @@ void SensorDevice::reconnect() { mReconnecting = false; } -bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList, - const Vector<sensor_t>& newSensorList) { +bool SensorDevice::sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList, + const std::vector<sensor_t>& newSensorList) { bool didChange = false; if (oldSensorList.size() != newSensorList.size()) { @@ -375,19 +380,17 @@ bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList, bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) { bool equivalent = true; if (prevSensor.handle != newSensor.handle || - (strcmp(prevSensor.vendor, newSensor.vendor) != 0) || - (strcmp(prevSensor.stringType, newSensor.stringType) != 0) || - (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) || - (prevSensor.version != newSensor.version) || - (prevSensor.type != newSensor.type) || - (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) || - (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) || - (std::abs(prevSensor.power - newSensor.power) > 0.001f) || - (prevSensor.minDelay != newSensor.minDelay) || - (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) || - (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) || - (prevSensor.maxDelay != newSensor.maxDelay) || - (prevSensor.flags != newSensor.flags)) { + (strcmp(prevSensor.vendor, newSensor.vendor) != 0) || + (strcmp(prevSensor.stringType, newSensor.stringType) != 0) || + (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) || + (prevSensor.version != newSensor.version) || (prevSensor.type != newSensor.type) || + (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) || + (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) || + (std::abs(prevSensor.power - newSensor.power) > 0.001f) || + (prevSensor.minDelay != newSensor.minDelay) || + (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) || + (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) || + (prevSensor.maxDelay != newSensor.maxDelay) || (prevSensor.flags != newSensor.flags)) { equivalent = false; } return equivalent; @@ -405,7 +408,7 @@ void SensorDevice::reactivateSensors(const DefaultKeyedVector<int, Info>& previo for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& batchParams = info.batchParams[j]; status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */, - batchParams.mTSample, batchParams.mTBatch); + batchParams.mTSample, batchParams.mTBatch); if (res == NO_ERROR) { activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */); @@ -433,7 +436,7 @@ std::string SensorDevice::dump() const { mSensorList.size(), mActivationCount.size(), mDisabledClients.size()); Mutex::Autolock _l(mLock); - for (const auto & s : mSensorList) { + for (const auto& s : mSensorList) { int32_t handle = s.handle; const Info& info = mActivationCount.valueFor(handle); if (info.numActiveClients() == 0) continue; @@ -444,8 +447,9 @@ std::string SensorDevice::dump() const { for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& params = info.batchParams[j]; result.appendFormat("%.1f%s%s", params.mTSample / 1e6f, - isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "", - (j < info.batchParams.size() - 1) ? ", " : ""); + isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" + : "", + (j < info.batchParams.size() - 1) ? ", " : ""); } result.appendFormat("}, selected = %.2f ms; ", info.bestBatchParams.mTSample / 1e6f); @@ -453,8 +457,9 @@ std::string SensorDevice::dump() const { for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& params = info.batchParams[j]; result.appendFormat("%.1f%s%s", params.mTBatch / 1e6f, - isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "", - (j < info.batchParams.size() - 1) ? ", " : ""); + isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" + : "", + (j < info.batchParams.size() - 1) ? ", " : ""); } result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f); } @@ -472,29 +477,29 @@ std::string SensorDevice::dump() const { void SensorDevice::dump(ProtoOutputStream* proto) const { using namespace service::SensorDeviceProto; if (mSensors == nullptr) { - proto->write(INITIALIZED , false); + proto->write(INITIALIZED, false); return; } - proto->write(INITIALIZED , true); - proto->write(TOTAL_SENSORS , int(mSensorList.size())); - proto->write(ACTIVE_SENSORS , int(mActivationCount.size())); + proto->write(INITIALIZED, true); + proto->write(TOTAL_SENSORS, int(mSensorList.size())); + proto->write(ACTIVE_SENSORS, int(mActivationCount.size())); Mutex::Autolock _l(mLock); - for (const auto & s : mSensorList) { + for (const auto& s : mSensorList) { int32_t handle = s.handle; const Info& info = mActivationCount.valueFor(handle); if (info.numActiveClients() == 0) continue; uint64_t token = proto->start(SENSORS); - proto->write(SensorProto::HANDLE , handle); - proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size())); + proto->write(SensorProto::HANDLE, handle); + proto->write(SensorProto::ACTIVE_COUNT, int(info.batchParams.size())); for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& params = info.batchParams[j]; - proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f); - proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f); + proto->write(SensorProto::SAMPLING_PERIOD_MS, params.mTSample / 1e6f); + proto->write(SensorProto::BATCHING_PERIOD_MS, params.mTBatch / 1e6f); } - proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f); - proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f); + proto->write(SensorProto::SAMPLING_PERIOD_SELECTED, info.bestBatchParams.mTSample / 1e6f); + proto->write(SensorProto::BATCHING_PERIOD_SELECTED, info.bestBatchParams.mTBatch / 1e6f); proto->end(token); } } @@ -531,20 +536,19 @@ ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) { do { auto ret = mSensors->poll( - count, - [&](auto result, - const auto &events, - const auto &dynamicSensorsAdded) { + count, [&](auto result, const auto& events, const auto& dynamicSensorsAdded) { if (result == Result::OK) { convertToSensorEventsAndQuantize(convertToNewEvents(events), - convertToNewSensorInfos(dynamicSensorsAdded), buffer); + convertToNewSensorInfos( + dynamicSensorsAdded), + buffer); err = (ssize_t)events.size(); } else { err = statusFromResult(result); } }); - if (ret.isOk()) { + if (ret.isOk()) { hidlTransportError = false; } else { hidlTransportError = true; @@ -559,7 +563,7 @@ ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) { } } while (hidlTransportError); - if(numHidlTransportErrors > 0) { + if (numHidlTransportErrors > 0) { ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors); HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors); mHidlTransportErrors.add(errLog); @@ -581,7 +585,8 @@ ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead // events is not available, then read() would return no events, possibly introducing // additional latency in delivering events to applications. mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) | - asBaseType(INTERNAL_WAKE), &eventFlagState); + asBaseType(INTERNAL_WAKE), + &eventFlagState); availableEvents = mSensors->getEventQueue()->availableToRead(); if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) { @@ -600,47 +605,39 @@ ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead for (size_t i = 0; i < eventsToRead; i++) { convertToSensorEvent(mEventBuffer[i], &buffer[i]); android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i], - getResolutionForSensor(buffer[i].sensor)); + getResolutionForSensor( + buffer[i].sensor)); } eventsRead = eventsToRead; } else { - ALOGW("Failed to read %zu events, currently %zu events available", - eventsToRead, availableEvents); + ALOGW("Failed to read %zu events, currently %zu events available", eventsToRead, + availableEvents); } } return eventsRead; } -Return<void> SensorDevice::onDynamicSensorsConnected( - const hidl_vec<SensorInfo> &dynamicSensorsAdded) { +void SensorDevice::onDynamicSensorsConnected(const std::vector<sensor_t>& 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) { - const SensorInfo &info = dynamicSensorsAdded[i]; + const sensor_t& sensor = dynamicSensorsAdded[i]; - auto it = mConnectedDynamicSensors.find(info.sensorHandle); + auto it = mConnectedDynamicSensors.find(sensor.handle); CHECK(it == mConnectedDynamicSensors.end()); - sensor_t *sensor = new sensor_t(); - convertToSensor(convertToOldSensorInfo(info), sensor); - - mConnectedDynamicSensors.insert( - std::make_pair(sensor->handle, sensor)); + mConnectedDynamicSensors.insert(std::make_pair(sensor.handle, sensor)); } mDynamicSensorsCv.notify_all(); - - return Return<void>(); } -Return<void> SensorDevice::onDynamicSensorsDisconnected( - const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) { - (void) dynamicSensorHandlesRemoved; +void SensorDevice::onDynamicSensorsDisconnected( + const std::vector<int32_t>& /* dynamicSensorHandlesRemoved */) { // TODO: Currently dynamic sensors do not seem to be removed - return Return<void>(); } void SensorDevice::writeWakeLockHandled(uint32_t count) { @@ -653,7 +650,7 @@ void SensorDevice::writeWakeLockHandled(uint32_t count) { } } -void SensorDevice::autoDisable(void *ident, int handle) { +void SensorDevice::autoDisable(void* ident, int handle) { Mutex::Autolock _l(mLock); ssize_t activationIndex = mActivationCount.indexOfKey(handle); if (activationIndex < 0) { @@ -687,15 +684,15 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { Info& info(mActivationCount.editValueAt(activationIndex)); ALOGD_IF(DEBUG_CONNECTIONS, - "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", - ident, handle, enabled, info.batchParams.size()); + "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", ident, + handle, enabled, info.batchParams.size()); if (enabled) { ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident)); if (isClientDisabledLocked(ident)) { - ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d", - ident, handle); + ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d", ident, + handle); return NO_ERROR; } @@ -714,7 +711,6 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { // dictionary. auto it = mConnectedDynamicSensors.find(handle); if (it != mConnectedDynamicSensors.end()) { - delete it->second; mConnectedDynamicSensors.erase(it); } @@ -726,11 +722,10 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { // Call batch for this sensor with the previously calculated best effort // batch_rate and timeout. One of the apps has unregistered for sensor // events, and the best effort batch parameters might have changed. - ALOGD_IF(DEBUG_CONNECTIONS, - "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, handle, - info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch); - checkReturn(mSensors->batch( - handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch)); + ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, + handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch); + checkReturn(mSensors->batch(handle, info.bestBatchParams.mTSample, + info.bestBatchParams.mTBatch)); } } else { // sensor wasn't enabled for this ident @@ -767,12 +762,8 @@ status_t SensorDevice::doActivateHardwareLocked(int handle, bool enabled) { return err; } -status_t SensorDevice::batch( - void* ident, - int handle, - int flags, - int64_t samplingPeriodNs, - int64_t maxBatchReportLatencyNs) { +status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { if (mSensors == nullptr) return NO_INIT; if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) { @@ -783,7 +774,8 @@ status_t SensorDevice::batch( } ALOGD_IF(DEBUG_CONNECTIONS, - "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64, + "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 + " timeout=%" PRId64, ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); Mutex::Autolock _l(mLock); @@ -807,25 +799,24 @@ status_t SensorDevice::batchLocked(void* ident, int handle, int flags, int64_t s info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs); } - status_t err = updateBatchParamsLocked(handle, info); + status_t err = updateBatchParamsLocked(handle, info); if (err != NO_ERROR) { - ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s", - mSensors.get(), handle, info.bestBatchParams.mTSample, - info.bestBatchParams.mTBatch, strerror(-err)); + ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s", mSensors.get(), + handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch, strerror(-err)); info.removeBatchParamsForIdent(ident); } return err; } -status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) { +status_t SensorDevice::updateBatchParamsLocked(int handle, Info& info) { BatchParams prevBestBatchParams = info.bestBatchParams; // Find the minimum of all timeouts and batch_rates for this sensor. info.selectBatchParams(); ALOGD_IF(DEBUG_CONNECTIONS, - "\t>>> curr_period=%" PRId64 " min_period=%" PRId64 - " curr_timeout=%" PRId64 " min_timeout=%" PRId64, + "\t>>> curr_period=%" PRId64 " min_period=%" PRId64 " curr_timeout=%" PRId64 + " min_timeout=%" PRId64, prevBestBatchParams.mTSample, info.bestBatchParams.mTSample, prevBestBatchParams.mTBatch, info.bestBatchParams.mTBatch); @@ -834,8 +825,8 @@ status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) { if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) { ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch); - err = checkReturnAndGetStatus(mSensors->batch( - handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch)); + err = checkReturnAndGetStatus(mSensors->batch(handle, info.bestBatchParams.mTSample, + info.bestBatchParams.mTBatch)); } return err; @@ -866,8 +857,8 @@ bool SensorDevice::isClientDisabledLocked(void* ident) const { return mDisabledClients.count(ident) > 0; } -std::vector<void *> SensorDevice::getDisabledClientsLocked() const { - std::vector<void *> vec; +std::vector<void*> SensorDevice::getDisabledClientsLocked() const { + std::vector<void*> vec; for (const auto& it : mDisabledClients) { vec.push_back(it.first); } @@ -896,7 +887,7 @@ void SensorDevice::setUidStateForConnection(void* ident, SensorService::UidState addDisabledReasonForIdentLocked(ident, DisabledReason::DISABLED_REASON_UID_IDLE); } - for (size_t i = 0; i< mActivationCount.size(); ++i) { + for (size_t i = 0; i < mActivationCount.size(); ++i) { int handle = mActivationCount.keyAt(i); Info& info = mActivationCount.editValueAt(i); @@ -905,8 +896,7 @@ void SensorDevice::setUidStateForConnection(void* ident, SensorService::UidState bool disable = info.numActiveClients() == 0 && info.isActive; bool enable = info.numActiveClients() > 0 && !info.isActive; - if ((enable || disable) && - doActivateHardwareLocked(handle, enable) == NO_ERROR) { + if ((enable || disable) && doActivateHardwareLocked(handle, enable) == NO_ERROR) { info.isActive = enable; } } @@ -941,22 +931,21 @@ void SensorDevice::enableAllSensors() { if (mSensors == nullptr) return; Mutex::Autolock _l(mLock); - for (void *client : getDisabledClientsLocked()) { - removeDisabledReasonForIdentLocked( - client, DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); + for (void* client : getDisabledClientsLocked()) { + removeDisabledReasonForIdentLocked(client, + DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); } - for (size_t i = 0; i< mActivationCount.size(); ++i) { + for (size_t i = 0; i < mActivationCount.size(); ++i) { Info& info = mActivationCount.editValueAt(i); if (info.batchParams.isEmpty()) continue; info.selectBatchParams(); const int sensor_handle = mActivationCount.keyAt(i); ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ", - sensor_handle); - status_t err = checkReturnAndGetStatus(mSensors->batch( - sensor_handle, - info.bestBatchParams.mTSample, - info.bestBatchParams.mTBatch)); + sensor_handle); + status_t err = checkReturnAndGetStatus(mSensors->batch(sensor_handle, + info.bestBatchParams.mTSample, + info.bestBatchParams.mTBatch)); ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err)); if (err == NO_ERROR) { @@ -973,38 +962,36 @@ void SensorDevice::enableAllSensors() { void SensorDevice::disableAllSensors() { if (mSensors == nullptr) return; Mutex::Autolock _l(mLock); - for (size_t i = 0; i< mActivationCount.size(); ++i) { + for (size_t i = 0; i < mActivationCount.size(); ++i) { Info& info = mActivationCount.editValueAt(i); // Check if this sensor has been activated previously and disable it. if (info.batchParams.size() > 0) { - const int sensor_handle = mActivationCount.keyAt(i); - ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ", - sensor_handle); - checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */)); - - // Add all the connections that were registered for this sensor to the disabled - // clients list. - for (size_t j = 0; j < info.batchParams.size(); ++j) { - addDisabledReasonForIdentLocked( - info.batchParams.keyAt(j), DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); - ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j)); - } - - info.isActive = false; + const int sensor_handle = mActivationCount.keyAt(i); + ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ", + sensor_handle); + checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */)); + + // Add all the connections that were registered for this sensor to the disabled + // clients list. + for (size_t j = 0; j < info.batchParams.size(); ++j) { + addDisabledReasonForIdentLocked(info.batchParams.keyAt(j), + DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); + ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j)); + } + + info.isActive = false; } } } -status_t SensorDevice::injectSensorData( - const sensors_event_t *injected_sensor_event) { +status_t SensorDevice::injectSensorData(const sensors_event_t* injected_sensor_event) { if (mSensors == nullptr) return NO_INIT; ALOGD_IF(DEBUG_CONNECTIONS, - "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f", - injected_sensor_event->sensor, - injected_sensor_event->timestamp, injected_sensor_event->data[0], - injected_sensor_event->data[1], injected_sensor_event->data[2], - injected_sensor_event->data[3], injected_sensor_event->data[4], - injected_sensor_event->data[5]); + "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f", + injected_sensor_event->sensor, injected_sensor_event->timestamp, + injected_sensor_event->data[0], injected_sensor_event->data[1], + injected_sensor_event->data[2], injected_sensor_event->data[3], + injected_sensor_event->data[4], injected_sensor_event->data[5]); Event ev; V2_1::implementation::convertFromSensorEvent(*injected_sensor_event, &ev); @@ -1014,8 +1001,8 @@ status_t SensorDevice::injectSensorData( status_t SensorDevice::setMode(uint32_t mode) { if (mSensors == nullptr) return NO_INIT; - return checkReturnAndGetStatus(mSensors->setOperationMode( - static_cast<hardware::sensors::V1_0::OperationMode>(mode))); + return checkReturnAndGetStatus( + mSensors->setOperationMode(static_cast<hardware::sensors::V1_0::OperationMode>(mode))); } int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) { @@ -1041,21 +1028,20 @@ int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) format = SharedMemFormat::SENSORS_EVENT; SharedMemInfo mem = { - .type = type, - .format = format, - .size = static_cast<uint32_t>(memory->size), - .memoryHandle = memory->handle, + .type = type, + .format = format, + .size = static_cast<uint32_t>(memory->size), + .memoryHandle = memory->handle, }; int32_t ret; - checkReturn(mSensors->registerDirectChannel(mem, - [&ret](auto result, auto channelHandle) { - if (result == Result::OK) { - ret = channelHandle; - } else { - ret = statusFromResult(result); - } - })); + checkReturn(mSensors->registerDirectChannel(mem, [&ret](auto result, auto channelHandle) { + if (result == Result::OK) { + ret = channelHandle; + } else { + ret = statusFromResult(result); + } + })); return ret; } @@ -1065,13 +1051,13 @@ void SensorDevice::unregisterDirectChannel(int32_t channelHandle) { checkReturn(mSensors->unregisterDirectChannel(channelHandle)); } -int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, - int32_t channelHandle, const struct sensors_direct_cfg_t *config) { +int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle, + const struct sensors_direct_cfg_t* config) { if (mSensors == nullptr) return NO_INIT; Mutex::Autolock _l(mLock); RateLevel rate; - switch(config->rate_level) { + switch (config->rate_level) { case SENSOR_DIRECT_RATE_STOP: rate = RateLevel::STOP; break; @@ -1090,17 +1076,17 @@ int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t ret; checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate, - [&ret, rate] (auto result, auto token) { - if (rate == RateLevel::STOP) { - ret = statusFromResult(result); - } else { - if (result == Result::OK) { - ret = token; - } else { - ret = statusFromResult(result); - } - } - })); + [&ret, rate](auto result, auto token) { + if (rate == RateLevel::STOP) { + ret = statusFromResult(result); + } else { + if (result == Result::OK) { + ret = token; + } else { + ret = statusFromResult(result); + } + } + })); return ret; } @@ -1118,13 +1104,12 @@ int SensorDevice::Info::numActiveClients() const { return num; } -status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int, - int64_t samplingPeriodNs, +status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int, int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs) { ssize_t index = batchParams.indexOfKey(ident); if (index < 0) { - ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 - " timeout=%" PRId64 ") failed (%s)", + ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64 + ") failed (%s)", ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index)); return BAD_INDEX; } @@ -1168,12 +1153,11 @@ bool SensorDevice::isDirectReportSupported() const { return mIsDirectReportSupported; } -void SensorDevice::convertToSensorEvent( - const Event &src, sensors_event_t *dst) { +void SensorDevice::convertToSensorEvent(const Event& src, sensors_event_t* dst) { V2_1::implementation::convertToSensorEvent(src, dst); if (src.sensorType == V2_1::SensorType::DYNAMIC_SENSOR_META) { - const DynamicSensorInfo &dyn = src.u.dynamic; + const DynamicSensorInfo& dyn = src.u.dynamic; dst->dynamic_sensor_meta.connected = dyn.connected; dst->dynamic_sensor_meta.handle = dyn.sensorHandle; @@ -1184,56 +1168,53 @@ void SensorDevice::convertToSensorEvent( // marks it as oneway. auto it = mConnectedDynamicSensors.find(dyn.sensorHandle); if (it == mConnectedDynamicSensors.end()) { - mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT, - [&, dyn]{ - return mConnectedDynamicSensors.find(dyn.sensorHandle) - != 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; + dst->dynamic_sensor_meta.sensor = &it->second; - memcpy(dst->dynamic_sensor_meta.uuid, - dyn.uuid.data(), + memcpy(dst->dynamic_sensor_meta.uuid, dyn.uuid.data(), sizeof(dst->dynamic_sensor_meta.uuid)); } } } -void SensorDevice::convertToSensorEventsAndQuantize( - const hidl_vec<Event> &src, - const hidl_vec<SensorInfo> &dynamicSensorsAdded, - sensors_event_t *dst) { - - if (dynamicSensorsAdded.size() > 0) { - onDynamicSensorsConnected(dynamicSensorsAdded); +void SensorDevice::convertToSensorEventsAndQuantize(const hidl_vec<Event>& src, + const hidl_vec<SensorInfo>& dynamicSensorsAdded, + sensors_event_t* dst) { + if (dynamicSensorsAdded.size() > 0 && mCallback != nullptr) { + mCallback->onDynamicSensorsConnected_2_1(dynamicSensorsAdded); } for (size_t i = 0; i < src.size(); ++i) { V2_1::implementation::convertToSensorEvent(src[i], &dst[i]); android::SensorDeviceUtils::quantizeSensorEventValues(&dst[i], - getResolutionForSensor(dst[i].sensor)); + getResolutionForSensor( + dst[i].sensor)); } } float SensorDevice::getResolutionForSensor(int sensorHandle) { for (size_t i = 0; i < mSensorList.size(); i++) { - if (sensorHandle == mSensorList[i].handle) { - return mSensorList[i].resolution; - } + if (sensorHandle == mSensorList[i].handle) { + return mSensorList[i].resolution; + } } auto it = mConnectedDynamicSensors.find(sensorHandle); if (it != mConnectedDynamicSensors.end()) { - return it->second->resolution; + return it->second.resolution; } return 0; } -void SensorDevice::handleHidlDeath(const std::string & detail) { +void SensorDevice::handleHidlDeath(const std::string& detail) { if (!mSensors->supportsMessageQueues()) { // restart is the only option at present. LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str()); diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index bc8d20fbf6..314ddb8758 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -17,14 +17,14 @@ #ifndef ANDROID_SENSOR_DEVICE_H #define ANDROID_SENSOR_DEVICE_H +#include "ISensorsWrapper.h" #include "SensorDeviceUtils.h" #include "SensorService.h" #include "SensorServiceUtils.h" -#include "ISensorsWrapper.h" #include <fmq/MessageQueue.h> -#include <sensor/SensorEventQueue.h> #include <sensor/Sensor.h> +#include <sensor/SensorEventQueue.h> #include <stdint.h> #include <sys/types.h> #include <utils/KeyedVector.h> @@ -32,9 +32,10 @@ #include <utils/String8.h> #include <utils/Timers.h> +#include <algorithm> //std::max std::min #include <string> #include <unordered_map> -#include <algorithm> //std::max std::min +#include <vector> #include "RingBuffer.h" @@ -42,18 +43,18 @@ namespace android { +using Result = ::android::hardware::sensors::V1_0::Result; + // --------------------------------------------------------------------------- class SensorsHalDeathReceivier : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& service) override; }; -class SensorDevice : public Singleton<SensorDevice>, - public SensorServiceUtil::Dumpable { +class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable { public: class HidlTransportErrorLog { - public: - + public: HidlTransportErrorLog() { mTs = 0; mCount = 0; @@ -66,7 +67,7 @@ public: String8 toString() const { String8 result; - struct tm *timeInfo = localtime(&mTs); + struct tm* timeInfo = localtime(&mTs); result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec, mCount); return result; @@ -74,7 +75,7 @@ public: private: time_t mTs; // timestamp of the error - int mCount; // number of transport errors observed + int mCount; // number of transport errors observed }; ~SensorDevice(); @@ -99,29 +100,24 @@ public: status_t setMode(uint32_t mode); bool isDirectReportSupported() const; - int32_t registerDirectChannel(const sensors_direct_mem_t *memory); + int32_t registerDirectChannel(const sensors_direct_mem_t* memory); void unregisterDirectChannel(int32_t channelHandle); - int32_t configureDirectChannel(int32_t sensorHandle, - int32_t channelHandle, const struct sensors_direct_cfg_t *config); + int32_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle, + const struct sensors_direct_cfg_t* config); void disableAllSensors(); void enableAllSensors(); - void autoDisable(void *ident, int handle); + void autoDisable(void* ident, int handle); - status_t injectSensorData(const sensors_event_t *event); - void notifyConnectionDestroyed(void *ident); + status_t injectSensorData(const sensors_event_t* event); + void notifyConnectionDestroyed(void* ident); - using Result = ::android::hardware::sensors::V1_0::Result; - hardware::Return<void> onDynamicSensorsConnected( - const hardware::hidl_vec<hardware::sensors::V2_1::SensorInfo> &dynamicSensorsAdded); - hardware::Return<void> onDynamicSensorsDisconnected( - const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved); + void onDynamicSensorsConnected(const std::vector<sensor_t>& dynamicSensorsAdded); + void onDynamicSensorsDisconnected(const std::vector<int32_t>& dynamicSensorHandlesRemoved); void setUidStateForConnection(void* ident, SensorService::UidState state); - bool isReconnecting() const { - return mReconnecting; - } + bool isReconnecting() const { return mReconnecting; } bool isSensorActive(int handle) const; @@ -132,12 +128,14 @@ public: // Dumpable virtual std::string dump() const override; virtual void dump(util::ProtoOutputStream* proto) const override; + private: friend class Singleton<SensorDevice>; sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors; - Vector<sensor_t> mSensorList; - std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors; + sp<::android::hardware::sensors::V2_1::ISensorsCallback> mCallback; + std::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 @@ -147,26 +145,26 @@ private: 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 + static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz + mutable Mutex mLock; // protect mActivationCount[].batchParams // fixed-size array after construction // Struct to store all the parameters(samplingPeriod, maxBatchReportLatency and flags) from // batch call. For continous mode clients, maxBatchReportLatency is set to zero. struct BatchParams { - nsecs_t mTSample, mTBatch; - BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {} - BatchParams(nsecs_t tSample, nsecs_t tBatch): mTSample(tSample), mTBatch(tBatch) {} - bool operator != (const BatchParams& other) { - return !(mTSample == other.mTSample && mTBatch == other.mTBatch); - } - // Merge another parameter with this one. The updated mTSample will be the min of the two. - // The update mTBatch will be the min of original mTBatch and the apparent batch period - // of the other. the apparent batch is the maximum of mTBatch and mTSample, - void merge(const BatchParams &other) { - mTSample = std::min(mTSample, other.mTSample); - mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample)); - } + nsecs_t mTSample, mTBatch; + BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {} + BatchParams(nsecs_t tSample, nsecs_t tBatch) : mTSample(tSample), mTBatch(tBatch) {} + bool operator!=(const BatchParams& other) { + return !(mTSample == other.mTSample && mTBatch == other.mTBatch); + } + // Merge another parameter with this one. The updated mTSample will be the min of the two. + // The update mTBatch will be the min of original mTBatch and the apparent batch period + // of the other. the apparent batch is the maximum of mTBatch and mTSample, + void merge(const BatchParams& other) { + mTSample = std::min(mTSample, other.mTSample); + mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample)); + } }; // Store batch parameters in the KeyedVector and the optimal batch_rate and timeout in @@ -224,7 +222,7 @@ private: static_assert(DisabledReason::DISABLED_REASON_MAX < sizeof(uint8_t) * CHAR_BIT); // Use this map to determine which client is activated or deactivated. - std::unordered_map<void *, uint8_t> mDisabledClients; + std::unordered_map<void*, uint8_t> mDisabledClients; void addDisabledReasonForIdentLocked(void* ident, DisabledReason reason); void removeDisabledReasonForIdentLocked(void* ident, DisabledReason reason); @@ -233,13 +231,13 @@ private: bool connectHidlService(); void initializeSensorList(); void reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations); - static bool sensorHandlesChanged(const Vector<sensor_t>& oldSensorList, - const Vector<sensor_t>& newSensorList); + static bool sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList, + const std::vector<sensor_t>& newSensorList); static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor); enum HalConnectionStatus { - CONNECTED, // Successfully connected to the HAL - DOES_NOT_EXIST, // Could not find the HAL + CONNECTED, // Successfully connected to the HAL + DOES_NOT_EXIST, // Could not find the HAL FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize UNKNOWN, }; @@ -257,32 +255,32 @@ private: status_t updateBatchParamsLocked(int handle, Info& info); status_t doActivateHardwareLocked(int handle, bool enable); - void handleHidlDeath(const std::string &detail); - template<typename T> + void handleHidlDeath(const std::string& detail); + template <typename T> void checkReturn(const Return<T>& ret) { if (!ret.isOk()) { handleHidlDeath(ret.description()); } } + status_t checkReturnAndGetStatus(const Return<Result>& ret); - //TODO(b/67425500): remove waiter after bug is resolved. + // TODO(b/67425500): remove waiter after bug is resolved. sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter; bool isClientDisabled(void* ident) const; bool isClientDisabledLocked(void* ident) const; - std::vector<void *> getDisabledClientsLocked() const; + std::vector<void*> getDisabledClientsLocked() const; bool clientHasNoAccessLocked(void* ident) const; using Event = hardware::sensors::V2_1::Event; using SensorInfo = hardware::sensors::V2_1::SensorInfo; - void convertToSensorEvent(const Event &src, sensors_event_t *dst); + void convertToSensorEvent(const Event& src, sensors_event_t* dst); - void convertToSensorEventsAndQuantize( - const hardware::hidl_vec<Event> &src, - const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded, - sensors_event_t *dst); + void convertToSensorEventsAndQuantize(const hardware::hidl_vec<Event>& src, + const hardware::hidl_vec<SensorInfo>& dynamicSensorsAdded, + sensors_event_t* dst); float getResolutionForSensor(int sensorHandle); diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 32a0110a72..9bc7b8e30e 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -164,7 +164,6 @@ void SensorService::onFirstRef() { sensor_t const* list; ssize_t count = dev.getSensorList(&list); if (count > 0) { - ssize_t orientationIndex = -1; bool hasGyro = false, hasAccel = false, hasMag = false; uint32_t virtualSensorsNeeds = (1<<SENSOR_TYPE_GRAVITY) | @@ -183,9 +182,6 @@ void SensorService::onFirstRef() { case SENSOR_TYPE_MAGNETIC_FIELD: hasMag = true; break; - case SENSOR_TYPE_ORIENTATION: - orientationIndex = i; - break; case SENSOR_TYPE_GYROSCOPE: case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: hasGyro = true; @@ -201,6 +197,8 @@ void SensorService::onFirstRef() { virtualSensorsNeeds &= ~(1<<list[i].type); } break; + default: + break; } if (useThisSensor) { if (list[i].type == SENSOR_TYPE_PROXIMITY) { diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 56b8374eb0..fb42cc02eb 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -79,6 +79,7 @@ cc_defaults { "libperfetto_client_experimental", "librenderengine", "libserviceutils", + "libtonemap", "libtrace_proto", "libaidlcommonsupport", ], @@ -200,7 +201,7 @@ filegroup { "SurfaceFlinger.cpp", "SurfaceFlingerDefaultFactory.cpp", "SurfaceInterceptor.cpp", - "SurfaceTracing.cpp", + "Tracing/LayerTracing.cpp", "Tracing/TransactionProtoParser.cpp", "TransactionCallbackInvoker.cpp", "TunnelModeEnabledReporter.cpp", diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 8c4c8b7a78..a4c21f4caf 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -171,7 +171,7 @@ protected: // the mStateLock. ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0; - bool getAutoRefresh() const { return mAutoRefresh; } + bool getAutoRefresh() const { return mDrawingState.autoRefresh; } bool getSidebandStreamChanged() const { return mSidebandStreamChanged; } // Returns true if the next buffer should be presented at the expected present time @@ -182,7 +182,6 @@ protected: // specific logic virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0; - std::atomic<bool> mAutoRefresh{false}; std::atomic<bool> mSidebandStreamChanged{false}; private: diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 4e5d2d03b0..dec7cc0806 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -118,7 +118,7 @@ bool BufferQueueLayer::isBufferDue(nsecs_t expectedPresentTime) const { bool BufferQueueLayer::fenceHasSignaled() const { Mutex::Autolock lock(mQueueItemLock); - if (SurfaceFlinger::enableLatchUnsignaled) { + if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { return true; } @@ -216,7 +216,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t bool autoRefresh; status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh, &queuedBuffer, maxFrameNumberToAcquire); - mAutoRefresh = autoRefresh; + mDrawingState.autoRefresh = autoRefresh; if (updateResult == BufferQueue::PRESENT_LATER) { // Producer doesn't want buffer to be displayed yet. Signal a // layer update so we check again at the next opportunity. @@ -300,7 +300,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // Decrement the queued-frames count. Signal another event if we // have more frames pending. - if ((queuedBuffer && more_frames_pending) || mAutoRefresh) { + if ((queuedBuffer && more_frames_pending) || mDrawingState.autoRefresh) { mFlinger->onLayerUpdate(); } @@ -523,7 +523,7 @@ void BufferQueueLayer::gatherBufferInfo() { } sp<Layer> BufferQueueLayer::createClone() { - LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata()); + LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()); args.textureName = mTextureName; sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args); layer->setInitialValuesForClone(this); diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index dfdb5c055d..c6e0727806 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -62,6 +62,11 @@ public: status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format); sp<IGraphicBufferProducer> getProducer() const; + void setSizeForTest(uint32_t w, uint32_t h) { + mDrawingState.active_legacy.w = w; + mDrawingState.active_legacy.h = h; + } + protected: void gatherBufferInfo() override; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index c0753f9d47..b4ccb803e9 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -630,7 +630,7 @@ FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) c // Interface implementation for BufferLayer // ----------------------------------------------------------------------- bool BufferStateLayer::fenceHasSignaled() const { - if (SurfaceFlinger::enableLatchUnsignaled) { + if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { return true; } @@ -660,9 +660,7 @@ bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) { } void BufferStateLayer::setAutoRefresh(bool autoRefresh) { - if (!mAutoRefresh.exchange(autoRefresh)) { - mFlinger->onLayerUpdate(); - } + mDrawingState.autoRefresh = autoRefresh; } bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { @@ -885,7 +883,7 @@ Rect BufferStateLayer::computeBufferCrop(const State& s) { } sp<Layer> BufferStateLayer::createClone() { - LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata()); + LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()); args.textureName = mTextureName; sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args); layer->mHwcSlotGenerator = mHwcSlotGenerator; diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 8da2e24aa4..0a8ebec9f3 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -72,35 +72,28 @@ sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const return lbc; } -status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, - uint32_t flags, const sp<IBinder>& parentHandle, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, - uint32_t* outTransformHint) { +status_t Client::createSurface(const String8& name, uint32_t /* w */, uint32_t /* h */, + PixelFormat /* format */, uint32_t flags, + const sp<IBinder>& parentHandle, LayerMetadata metadata, + sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* /* gbp */, + int32_t* outLayerId, uint32_t* outTransformHint) { // We rely on createLayer to check permissions. - return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp, - parentHandle, outLayerId, nullptr, outTransformHint); + LayerCreationArgs args(mFlinger.get(), this, name.c_str(), flags, std::move(metadata)); + return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, nullptr, + outTransformHint); } -status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, - PixelFormat format, uint32_t flags, - const sp<IGraphicBufferProducer>& parent, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, - uint32_t* outTransformHint) { - if (mFlinger->authenticateSurfaceTexture(parent) == false) { - ALOGE("failed to authenticate surface texture"); - return BAD_VALUE; - } - - const auto& layer = (static_cast<MonitoredProducer*>(parent.get()))->getLayer(); - if (layer == nullptr) { - ALOGE("failed to find parent layer"); - return BAD_VALUE; - } - - return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp, - nullptr, outLayerId, layer, outTransformHint); +status_t Client::createWithSurfaceParent(const String8& /* name */, uint32_t /* w */, + uint32_t /* h */, PixelFormat /* format */, + uint32_t /* flags */, + const sp<IGraphicBufferProducer>& /* parent */, + LayerMetadata /* metadata */, sp<IBinder>* /* handle */, + sp<IGraphicBufferProducer>* /* gbp */, + int32_t* /* outLayerId */, + uint32_t* /* outTransformHint */) { + // This api does not make sense with blast since SF no longer tracks IGBP. This api should be + // removed. + return BAD_VALUE; } status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 83b4a2552b..aefc014062 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -38,6 +38,7 @@ cc_defaults { static_libs: [ "libmath", "librenderengine", + "libtonemap", "libtrace_proto", "libaidlcommonsupport", ], diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp index 841e79f8af..3ccc229261 100644 --- a/services/surfaceflinger/ContainerLayer.cpp +++ b/services/surfaceflinger/ContainerLayer.cpp @@ -36,8 +36,7 @@ bool ContainerLayer::isVisible() const { sp<Layer> ContainerLayer::createClone() { sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer( - LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, - LayerMetadata())); + LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata())); layer->setInitialValuesForClone(this); return layer; } diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index e26ab117b8..82a9ae2578 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -326,7 +326,7 @@ status_t VirtualDisplaySurface::setAsyncMode(bool async) { status_t VirtualDisplaySurface::dequeueBuffer(Source source, PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) { - LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId)); + LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); status_t result = mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight, @@ -641,7 +641,7 @@ void VirtualDisplaySurface::resetPerFrameState() { } status_t VirtualDisplaySurface::refreshOutputBuffer() { - LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId)); + LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); if (mOutputProducerSlot >= 0) { mSource[SOURCE_SINK]->cancelBuffer( diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp index 86c6b2161c..845176c112 100644 --- a/services/surfaceflinger/EffectLayer.cpp +++ b/services/surfaceflinger/EffectLayer.cpp @@ -136,8 +136,7 @@ ui::Dataspace EffectLayer::getDataSpace() const { sp<Layer> EffectLayer::createClone() { sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer( - LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, - LayerMetadata())); + LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata())); layer->setInitialValuesForClone(this); return layer; } diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h index bd7b9a5222..438b1aa362 100644 --- a/services/surfaceflinger/FpsReporter.h +++ b/services/surfaceflinger/FpsReporter.h @@ -24,6 +24,7 @@ #include "Clock.h" #include "FrameTimeline/FrameTimeline.h" +#include "WpHash.h" namespace android { @@ -50,11 +51,6 @@ public: private: mutable std::mutex mMutex; - struct WpHash { - size_t operator()(const wp<IBinder>& p) const { - return std::hash<IBinder*>()(p.unsafe_get()); - } - }; struct TrackedListener { sp<gui::IFpsListener> listener; diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h index 671395f8de..4ada2b6372 100644 --- a/services/surfaceflinger/HdrLayerInfoReporter.h +++ b/services/surfaceflinger/HdrLayerInfoReporter.h @@ -22,6 +22,8 @@ #include <unordered_map> +#include "WpHash.h" + namespace android { class HdrLayerInfoReporter final : public IBinder::DeathRecipient { @@ -63,11 +65,6 @@ public: private: mutable std::mutex mMutex; - struct WpHash { - size_t operator()(const wp<IBinder>& p) const { - return std::hash<IBinder*>()(p.unsafe_get()); - } - }; struct TrackedListener { sp<gui::IHdrLayerInfoListener> listener; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 388181c231..968a49d526 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -87,11 +87,12 @@ using gui::WindowInfo; std::atomic<int32_t> Layer::sSequence{1}; Layer::Layer(const LayerCreationArgs& args) - : mFlinger(args.flinger), + : sequence(args.sequence.value_or(sSequence++)), + mFlinger(args.flinger), mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)), mClientRef(args.client), - mWindowType( - static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) { + mWindowType(static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))), + mLayerCreationFlags(args.flags) { uint32_t layerFlags = 0; if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque; @@ -99,8 +100,6 @@ Layer::Layer(const LayerCreationArgs& args) if (args.flags & ISurfaceComposerClient::eSkipScreenshot) layerFlags |= layer_state_t::eLayerSkipScreenshot; - mDrawingState.active_legacy.w = args.w; - mDrawingState.active_legacy.h = args.h; mDrawingState.flags = layerFlags; mDrawingState.active_legacy.transform.set(0, 0); mDrawingState.crop.makeInvalid(); @@ -185,12 +184,10 @@ Layer::~Layer() { } LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name, - uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata) + uint32_t flags, LayerMetadata metadata) : flinger(flinger), client(std::move(client)), name(std::move(name)), - w(w), - h(h), flags(flags), metadata(std::move(metadata)) { IPCThreadState* ipc = IPCThreadState::self(); @@ -420,8 +417,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(); } @@ -483,6 +478,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() { @@ -886,7 +884,7 @@ bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace da uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect; std::string name = mName + "BackgroundColorLayer"; mDrawingState.bgColorLayer = mFlinger->getFactory().createEffectLayer( - LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), 0, 0, flags, + LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), flags, LayerMetadata())); // add to child list @@ -923,8 +921,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); @@ -957,6 +958,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); @@ -1157,9 +1163,6 @@ void Layer::updateTreeHasFrameRateVote() { } bool Layer::setFrameRate(FrameRate frameRate) { - if (!mFlinger->useFrameRateApi) { - return false; - } if (mDrawingState.frameRate == frameRate) { return false; } @@ -2002,7 +2005,7 @@ LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags, writeToProtoDrawingState(layerProto, traceFlags, display); writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags); - if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) { + if (traceFlags & LayerTracing::TRACE_COMPOSITION) { // Only populate for the primary display. if (display) { const Hwc2::IComposerClient::Composition compositionType = getCompositionType(*display); @@ -2020,43 +2023,38 @@ LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags, void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, const DisplayDevice* display) { const ui::Transform transform = getTransform(); + auto buffer = getBuffer(); + if (buffer != nullptr) { + LayerProtoHelper::writeToProto(buffer, + [&]() { return layerInfo->mutable_active_buffer(); }); + LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()), + layerInfo->mutable_buffer_transform()); + } + layerInfo->set_invalidate(contentDirty); + layerInfo->set_is_protected(isProtected()); + layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); + layerInfo->set_queued_frames(getQueuedFrameCount()); + layerInfo->set_refresh_pending(isBufferLatched()); + 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::writeToProtoDeprecated(transform, layerInfo->mutable_transform()); + LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), + [&]() { return layerInfo->mutable_position(); }); + LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); + if (traceFlags & LayerTracing::TRACE_COMPOSITION) { + LayerProtoHelper::writeToProto(getVisibleRegion(display), + [&]() { return layerInfo->mutable_visible_region(); }); + } + LayerProtoHelper::writeToProto(surfaceDamageRegion, + [&]() { return layerInfo->mutable_damage_region(); }); - if (traceFlags & SurfaceTracing::TRACE_CRITICAL) { - - auto buffer = getBuffer(); - if (buffer != nullptr) { - LayerProtoHelper::writeToProto(buffer, - [&]() { return layerInfo->mutable_active_buffer(); }); - LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()), - layerInfo->mutable_buffer_transform()); - } - layerInfo->set_invalidate(contentDirty); - layerInfo->set_is_protected(isProtected()); - layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); - layerInfo->set_queued_frames(getQueuedFrameCount()); - layerInfo->set_refresh_pending(isBufferLatched()); - 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::writeToProtoDeprecated(transform, layerInfo->mutable_transform()); - LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), - [&]() { return layerInfo->mutable_position(); }); - LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); - if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) { - LayerProtoHelper::writeToProto(getVisibleRegion(display), - [&]() { return layerInfo->mutable_visible_region(); }); - } - LayerProtoHelper::writeToProto(surfaceDamageRegion, - [&]() { return layerInfo->mutable_damage_region(); }); - - if (hasColorTransform()) { - LayerProtoHelper::writeToProto(getColorTransform(), - layerInfo->mutable_color_transform()); - } + if (hasColorTransform()) { + LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform()); } LayerProtoHelper::writeToProto(mSourceBounds, @@ -2076,70 +2074,66 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet ui::Transform requestedTransform = state.transform; - if (traceFlags & SurfaceTracing::TRACE_CRITICAL) { - layerInfo->set_id(sequence); - layerInfo->set_name(getName().c_str()); - layerInfo->set_type(getType()); + layerInfo->set_id(sequence); + layerInfo->set_name(getName().c_str()); + layerInfo->set_type(getType()); - for (const auto& child : children) { - layerInfo->add_children(child->sequence); - } + for (const auto& child : children) { + layerInfo->add_children(child->sequence); + } - for (const wp<Layer>& weakRelative : state.zOrderRelatives) { - sp<Layer> strongRelative = weakRelative.promote(); - if (strongRelative != nullptr) { - layerInfo->add_relatives(strongRelative->sequence); - } + for (const wp<Layer>& weakRelative : state.zOrderRelatives) { + sp<Layer> strongRelative = weakRelative.promote(); + if (strongRelative != nullptr) { + layerInfo->add_relatives(strongRelative->sequence); } + } - LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, - [&]() { return layerInfo->mutable_transparent_region(); }); - - layerInfo->set_layer_stack(getLayerStack().id); - layerInfo->set_z(state.z); + LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, + [&]() { return layerInfo->mutable_transparent_region(); }); - LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), - [&]() { - return layerInfo->mutable_requested_position(); - }); + layerInfo->set_layer_stack(getLayerStack().id); + layerInfo->set_z(state.z); - LayerProtoHelper::writeSizeToProto(state.width, state.height, - [&]() { return layerInfo->mutable_size(); }); + LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() { + return layerInfo->mutable_requested_position(); + }); - LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); }); + LayerProtoHelper::writeSizeToProto(state.width, state.height, + [&]() { return layerInfo->mutable_size(); }); - layerInfo->set_is_opaque(isOpaque(state)); + LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); }); + layerInfo->set_is_opaque(isOpaque(state)); - layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat())); - LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); }); - LayerProtoHelper::writeToProto(state.color, - [&]() { return layerInfo->mutable_requested_color(); }); - layerInfo->set_flags(state.flags); + layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat())); + LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); }); + LayerProtoHelper::writeToProto(state.color, + [&]() { return layerInfo->mutable_requested_color(); }); + layerInfo->set_flags(state.flags); - LayerProtoHelper::writeToProtoDeprecated(requestedTransform, - layerInfo->mutable_requested_transform()); + LayerProtoHelper::writeToProtoDeprecated(requestedTransform, + layerInfo->mutable_requested_transform()); - auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); - if (parent != nullptr) { - layerInfo->set_parent(parent->sequence); - } else { - layerInfo->set_parent(-1); - } + auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); + if (parent != nullptr) { + layerInfo->set_parent(parent->sequence); + } else { + layerInfo->set_parent(-1); + } - auto zOrderRelativeOf = state.zOrderRelativeOf.promote(); - if (zOrderRelativeOf != nullptr) { - layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence); - } else { - layerInfo->set_z_order_relative_of(-1); - } + auto zOrderRelativeOf = state.zOrderRelativeOf.promote(); + if (zOrderRelativeOf != nullptr) { + layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence); + } else { + layerInfo->set_z_order_relative_of(-1); + } - layerInfo->set_is_relative_of(state.isRelativeOf); + layerInfo->set_is_relative_of(state.isRelativeOf); - layerInfo->set_owner_uid(mOwnerUid); - } + layerInfo->set_owner_uid(mOwnerUid); - if (traceFlags & SurfaceTracing::TRACE_INPUT) { + if (traceFlags & LayerTracing::TRACE_INPUT) { WindowInfo info; if (useDrawing) { info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true); @@ -2151,7 +2145,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet [&]() { return layerInfo->mutable_input_window_info(); }); } - if (traceFlags & SurfaceTracing::TRACE_EXTRA) { + if (traceFlags & LayerTracing::TRACE_EXTRA) { auto protoMap = layerInfo->mutable_metadata(); for (const auto& entry : state.metadata.mMap) { (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend()); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index bf338c18ad..bda1c28d27 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -57,7 +57,7 @@ #include "Scheduler/LayerInfo.h" #include "Scheduler/Seamlessness.h" #include "SurfaceFlinger.h" -#include "SurfaceTracing.h" +#include "Tracing/LayerTracing.h" #include "TransactionCallbackInvoker.h" using namespace android::surfaceflinger; @@ -85,20 +85,18 @@ class SurfaceFrame; } // namespace frametimeline struct LayerCreationArgs { - LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h, - uint32_t flags, LayerMetadata); + LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata); SurfaceFlinger* flinger; const sp<Client> client; std::string name; - uint32_t w; - uint32_t h; uint32_t flags; LayerMetadata metadata; pid_t callingPid; uid_t callingUid; uint32_t textureName; + std::optional<uint32_t> sequence = std::nullopt; }; class Layer : public virtual RefBase, compositionengine::LayerFE { @@ -280,6 +278,8 @@ public: sp<IBinder> releaseBufferEndpoint; gui::DropInputMode dropInputMode; + + bool autoRefresh = false; }; /* @@ -693,7 +693,7 @@ public: // external mStateLock. If writing drawing state, this function should be called on the // main or tracing thread. void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet, - uint32_t traceFlags = SurfaceTracing::TRACE_ALL); + uint32_t traceFlags = LayerTracing::TRACE_ALL); gui::WindowInfo::Type getWindowType() const { return mWindowType; } @@ -879,7 +879,7 @@ public: // Layer serial number. This gives layers an explicit ordering, so we // have a stable sort order when their layer stack and Z-order are // the same. - int32_t sequence{sSequence++}; + const int32_t sequence; bool mPendingHWCDestroy{false}; @@ -1117,6 +1117,8 @@ private: const std::vector<BlurRegion> getBlurRegions() const; bool mIsAtRoot = false; + + uint32_t mLayerCreationFlags; }; std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate); diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp index e84508febc..a1e14559e9 100644 --- a/services/surfaceflinger/LayerRenderArea.cpp +++ b/services/surfaceflinger/LayerRenderArea.cpp @@ -94,16 +94,28 @@ 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()); - uint32_t h = static_cast<uint32_t>(getHeight()); // In the "childrenOnly" case we reparent the children to a screenshot // layer which has no properties set and which does not draw. sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer( - {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()}); + {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()}); ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop); drawLayers(); diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index 6b2d745998..df76f50112 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -33,13 +33,7 @@ MonitoredProducer::MonitoredProducer(const sp<IGraphicBufferProducer>& producer, mFlinger(flinger), mLayer(layer) {} -MonitoredProducer::~MonitoredProducer() { - // Remove ourselves from SurfaceFlinger's list. We do this asynchronously - // because we don't know where this destructor is called from. It could be - // called with the mStateLock held, leading to a dead-lock (it actually - // happens). - mFlinger->removeGraphicBufferProducerAsync(onAsBinder()); -} +MonitoredProducer::~MonitoredProducer() {} status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return mProducer->requestBuffer(slot, buf); diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index 2231853fcb..f715309b2e 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -30,6 +30,7 @@ #include <unordered_map> #include "Scheduler/OneShotTimer.h" +#include "WpHash.h" namespace android { @@ -88,11 +89,6 @@ private: sp<IRegionSamplingListener> listener; }; - struct WpHash { - size_t operator()(const wp<IBinder>& p) const { - return std::hash<IBinder*>()(p.unsafe_get()); - } - }; std::vector<float> sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation); diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index e07eae79c8..455289ff96 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -560,8 +560,8 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, } } -int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp, - nsecs_t deadlineTimestamp) const { +int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp, + nsecs_t expectedVSyncTimestamp) const { if (mTokenManager != nullptr) { return mTokenManager->generateTokenForPredictions( {timestamp, deadlineTimestamp, expectedVSyncTimestamp}); @@ -586,7 +586,7 @@ void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) cons nsecs_t expectedVSync = event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval; event.vsync.frameTimelines[currentIndex] = - {.vsyncId = generateToken(event.header.timestamp, expectedVSync, deadline), + {.vsyncId = generateToken(event.header.timestamp, deadline, expectedVSync), .deadlineTimestamp = deadline, .expectedVSyncTimestamp = expectedVSync}; } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 73ae5dc9e3..de435708a6 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -204,8 +204,8 @@ private: void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp, nsecs_t deadlineTimestamp) override; - int64_t generateToken(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp, - nsecs_t deadlineTimestamp) const; + int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp, + nsecs_t expectedVSyncTimestamp) const; void generateFrameTimeline(DisplayEventReceiver::Event& event) const; const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex); diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index 043a536216..a020e2c834 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -30,41 +30,30 @@ namespace android::impl { -void MessageQueue::Handler::dispatchComposite() { - if ((mEventMask.fetch_or(kComposite) & kComposite) == 0) { - mQueue.mLooper->sendMessage(this, Message(kComposite)); - } -} - -void MessageQueue::Handler::dispatchCommit(int64_t vsyncId, nsecs_t expectedVsyncTime) { - if ((mEventMask.fetch_or(kCommit) & kCommit) == 0) { +void MessageQueue::Handler::dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime) { + if (!mFramePending.exchange(true)) { mVsyncId = vsyncId; mExpectedVsyncTime = expectedVsyncTime; - mQueue.mLooper->sendMessage(this, Message(kCommit)); + mQueue.mLooper->sendMessage(this, Message()); } } bool MessageQueue::Handler::isFramePending() const { - constexpr auto kPendingMask = kCommit | kComposite; - return (mEventMask.load() & kPendingMask) != 0; + return mFramePending.load(); } -void MessageQueue::Handler::handleMessage(const Message& message) { +void MessageQueue::Handler::handleMessage(const Message&) { + mFramePending.store(false); + const nsecs_t frameTime = systemTime(); - switch (message.what) { - case kCommit: - mEventMask.fetch_and(~kCommit); - if (!mQueue.mCompositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) { - return; - } - // Composite immediately, rather than after pending tasks through scheduleComposite. - [[fallthrough]]; - case kComposite: - mEventMask.fetch_and(~kComposite); - mQueue.mCompositor.composite(frameTime); - mQueue.mCompositor.sample(); - break; + auto& compositor = mQueue.mCompositor; + + if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) { + return; } + + compositor.composite(frameTime); + compositor.sample(); } MessageQueue::MessageQueue(ICompositor& compositor) @@ -122,7 +111,7 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions( {targetWakeupTime, readyTime, vsyncTime}); - mHandler->dispatchCommit(vsyncId, vsyncTime); + mHandler->dispatchFrame(vsyncId, vsyncTime); } void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch, @@ -176,7 +165,7 @@ void MessageQueue::postMessage(sp<MessageHandler>&& handler) { mLooper->sendMessage(handler, Message()); } -void MessageQueue::scheduleCommit() { +void MessageQueue::scheduleFrame() { ATRACE_CALL(); { @@ -195,18 +184,14 @@ void MessageQueue::scheduleCommit() { .earliestVsync = mVsync.lastCallbackTime.count()}); } -void MessageQueue::scheduleComposite() { - mHandler->dispatchComposite(); -} - void MessageQueue::injectorCallback() { ssize_t n; DisplayEventReceiver::Event buffer[8]; while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) { for (int i = 0; i < n; i++) { if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - mHandler->dispatchCommit(buffer[i].vsync.vsyncId, - buffer[i].vsync.expectedVSyncTimestamp); + auto& vsync = buffer[i].vsync; + mHandler->dispatchFrame(vsync.vsyncId, vsync.expectedVSyncTimestamp); break; } } diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 2c908a6983..dd69d60580 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -71,8 +71,7 @@ public: virtual void setInjector(sp<EventThreadConnection>) = 0; virtual void waitMessage() = 0; virtual void postMessage(sp<MessageHandler>&&) = 0; - virtual void scheduleCommit() = 0; - virtual void scheduleComposite() = 0; + virtual void scheduleFrame() = 0; using Clock = std::chrono::steady_clock; virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0; @@ -83,11 +82,8 @@ namespace impl { class MessageQueue : public android::MessageQueue { protected: class Handler : public MessageHandler { - static constexpr uint32_t kCommit = 0b1; - static constexpr uint32_t kComposite = 0b10; - MessageQueue& mQueue; - std::atomic<uint32_t> mEventMask = 0; + std::atomic_bool mFramePending = false; std::atomic<int64_t> mVsyncId = 0; std::atomic<nsecs_t> mExpectedVsyncTime = 0; @@ -97,8 +93,7 @@ protected: bool isFramePending() const; - virtual void dispatchCommit(int64_t vsyncId, nsecs_t expectedVsyncTime); - void dispatchComposite(); + virtual void dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime); }; friend class Handler; @@ -147,8 +142,7 @@ public: void waitMessage() override; void postMessage(sp<MessageHandler>&&) override; - void scheduleCommit() override; - void scheduleComposite() override; + void scheduleFrame() override; std::optional<Clock::time_point> getScheduledFrameTime() const override; }; diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index aabd88a435..0d17b0ca94 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -744,7 +744,6 @@ void RefreshRateConfigs::initializeIdleTimer() { [getCallback] { if (const auto callback = getCallback()) callback->onExpired(); }); - mIdleTimer->start(); } } diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 53472efae9..058402461c 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -355,10 +355,22 @@ public: std::function<void()> kernelTimerExpired) { std::scoped_lock lock(mIdleTimerCallbacksMutex); mIdleTimerCallbacks.emplace(); - mIdleTimerCallbacks->platform.onReset = platformTimerReset; - mIdleTimerCallbacks->platform.onExpired = platformTimerExpired; - mIdleTimerCallbacks->kernel.onReset = kernelTimerReset; - mIdleTimerCallbacks->kernel.onExpired = kernelTimerExpired; + 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) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index eeea25df7d..4d72798086 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -117,16 +117,10 @@ private: } }; -Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs, - ISchedulerCallback& callback) - : Scheduler(configs, callback, - {.useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) { -} +Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, Options options) + : impl::MessageQueue(compositor), mOptions(options), mSchedulerCallback(callback) {} -Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs, - ISchedulerCallback& callback, Options options) - : Scheduler(createVsyncSchedule(configs->supportsKernelIdleTimer()), configs, callback, - createLayerHistory(), options) { +void Scheduler::startTimers() { using namespace sysprop; if (const int64_t millis = set_touch_timer_ms(0); millis > 0) { @@ -147,22 +141,6 @@ Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& confi } } -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), - mPredictedVsyncTracer( - base::GetBoolProperty("debug.sf.show_predicted_vsync", false) - ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch) - : nullptr) { - setRefreshRateConfigs(configs); - mSchedulerCallback.setVsyncEnabled(false); -} - Scheduler::~Scheduler() { // Ensure the OneShotTimer threads are joined before we start destroying state. mDisplayPowerTimer.reset(); @@ -170,7 +148,13 @@ Scheduler::~Scheduler() { mRefreshRateConfigs.reset(); } -Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) { +void Scheduler::run() { + while (true) { + waitMessage(); + } +} + +void Scheduler::createVsyncSchedule(bool supportKernelTimer) { auto clock = std::make_unique<scheduler::SystemClock>(); auto tracker = createVSyncTracker(); auto dispatch = createVSyncDispatch(*tracker); @@ -180,11 +164,11 @@ Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) auto controller = std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit, supportKernelTimer); - return {std::move(controller), std::move(tracker), std::move(dispatch)}; -} + mVsyncSchedule = {std::move(controller), std::move(tracker), std::move(dispatch)}; -std::unique_ptr<LayerHistory> Scheduler::createLayerHistory() { - return std::make_unique<scheduler::LayerHistory>(); + if (base::GetBoolProperty("debug.sf.show_predicted_vsync", false)) { + mPredictedVsyncTracer = std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch); + } } std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( @@ -594,11 +578,11 @@ void Scheduler::registerLayer(Layer* layer) { // If the content detection feature is off, we still keep the layer history, // since we use it for other features (like Frame Rate API), so layers // still need to be registered. - mLayerHistory->registerLayer(layer, voteType); + mLayerHistory.registerLayer(layer, voteType); } void Scheduler::deregisterLayer(Layer* layer) { - mLayerHistory->deregisterLayer(layer); + mLayerHistory.deregisterLayer(layer); } void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, @@ -608,11 +592,11 @@ void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, if (!mRefreshRateConfigs->canSwitch()) return; } - mLayerHistory->record(layer, presentTime, systemTime(), updateType); + mLayerHistory.record(layer, presentTime, systemTime(), updateType); } void Scheduler::setModeChangePending(bool pending) { - mLayerHistory->setModeChangePending(pending); + mLayerHistory.setModeChangePending(pending); } void Scheduler::chooseRefreshRateForContent() { @@ -625,7 +609,7 @@ void Scheduler::chooseRefreshRateForContent() { const auto refreshRateConfigs = holdRefreshRateConfigs(); scheduler::LayerHistory::Summary summary = - mLayerHistory->summarize(*refreshRateConfigs, systemTime()); + mLayerHistory.summarize(*refreshRateConfigs, systemTime()); scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; DisplayModePtr newMode; bool frameRateChanged; @@ -686,7 +670,7 @@ void Scheduler::setDisplayPowerState(bool normal) { // Display Power event will boost the refresh rate to performance. // Clear Layer History to get fresh FPS detection - mLayerHistory->clear(); + mLayerHistory.clear(); } void Scheduler::kernelIdleTimerCallback(TimerState state) { @@ -730,7 +714,7 @@ void Scheduler::touchTimerCallback(TimerState state) { // NOTE: Instead of checking all the layers, we should be checking the layer // that is currently on top. b/142507166 will give us this capability. if (handleTimerStateChanged(&mFeatures.touch, touch)) { - mLayerHistory->clear(); + mLayerHistory.clear(); } ATRACE_INT("TouchState", static_cast<int>(touch)); } @@ -747,7 +731,7 @@ void Scheduler::dump(std::string& result) const { mTouchTimer ? mTouchTimer->dump().c_str() : "off"); StringAppendF(&result, "+ Content detection: %s %s\n\n", toContentDetectionString(mOptions.useContentDetection), - mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)"); + mLayerHistory.dump().c_str()); { std::lock_guard lock(mFrameRateOverridesLock); @@ -911,7 +895,7 @@ void Scheduler::onPostComposition(nsecs_t presentTime) { } void Scheduler::onActiveDisplayAreaChanged(uint32_t displayArea) { - mLayerHistory->setDisplayArea(displayArea); + mLayerHistory.setDisplayArea(displayArea); } void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 8397738160..2a6de54e93 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -18,6 +18,7 @@ #include <atomic> #include <functional> +#include <future> #include <memory> #include <mutex> #include <optional> @@ -32,6 +33,7 @@ #include "EventThread.h" #include "LayerHistory.h" +#include "MessageQueue.h" #include "OneShotTimer.h" #include "RefreshRateConfigs.h" #include "SchedulerUtils.h" @@ -70,14 +72,41 @@ protected: ~ISchedulerCallback() = default; }; -class Scheduler { +class Scheduler : impl::MessageQueue { + using Impl = impl::MessageQueue; + public: using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; using ModeEvent = scheduler::RefreshRateConfigEvent; - Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&); + struct Options { + // Whether to use content detection at all. + bool useContentDetection; + }; + + Scheduler(ICompositor&, ISchedulerCallback&, Options); ~Scheduler(); + void createVsyncSchedule(bool supportKernelIdleTimer); + void startTimers(); + void run(); + + using Impl::initVsync; + using Impl::setInjector; + + using Impl::getScheduledFrameTime; + using Impl::setDuration; + + using Impl::scheduleFrame; + + // Schedule an asynchronous or synchronous task on the main thread. + template <typename F, typename T = std::invoke_result_t<F>> + [[nodiscard]] std::future<T> schedule(F&& f) { + auto [task, future] = makeTask(std::move(f)); + postMessage(std::move(task)); + return std::move(future); + } + using ConnectionHandle = scheduler::ConnectionHandle; ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*, std::chrono::nanoseconds workDuration, @@ -184,17 +213,32 @@ public: void setRefreshRateConfigs(std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs) EXCLUDES(mRefreshRateConfigsLock) { - 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); - }); + // 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) { @@ -213,27 +257,12 @@ private: enum class TimerState { Reset, Expired }; enum class TouchState { Inactive, Active }; - struct Options { - // Whether to use content detection at all. - bool useContentDetection; - }; - struct VsyncSchedule { std::unique_ptr<scheduler::VsyncController> controller; std::unique_ptr<scheduler::VSyncTracker> tracker; std::unique_ptr<scheduler::VSyncDispatch> dispatch; }; - // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers. - Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&, Options); - - // Used by tests to inject mocks. - 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(); - // Create a connection on the given EventThread. ConnectionHandle createConnection(std::unique_ptr<EventThread>); sp<EventThreadConnection> createConnectionInternal( @@ -297,7 +326,7 @@ private: VsyncSchedule mVsyncSchedule; // Used to choose refresh rate if content detection is enabled. - std::unique_ptr<LayerHistory> mLayerHistory; + LayerHistory mLayerHistory; // Timer used to monitor touch events. std::optional<scheduler::OneShotTimer> mTouchTimer; @@ -338,7 +367,7 @@ private: GUARDED_BY(mVsyncTimelineLock); static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms; - const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer; + std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer; // 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 diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h index b2b0451666..2000c546a0 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.h +++ b/services/surfaceflinger/Scheduler/VsyncModulator.h @@ -25,6 +25,8 @@ #include <binder/IBinder.h> #include <utils/Timers.h> +#include "../WpHash.h" + namespace android::scheduler { // State machine controlled by transaction flags. VsyncModulator switches to early phase offsets @@ -124,12 +126,6 @@ private: using Schedule = TransactionSchedule; std::atomic<Schedule> mTransactionSchedule = Schedule::Late; - struct WpHash { - size_t operator()(const wp<IBinder>& p) const { - return std::hash<IBinder*>()(p.unsafe_get()); - } - }; - std::unordered_set<wp<IBinder>, WpHash> mEarlyWakeupRequests GUARDED_BY(mMutex); std::atomic<bool> mRefreshRateChangePending = false; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 48c3c108c1..45e0a3f616 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -125,7 +125,6 @@ #include "Scheduler/DispSyncSource.h" #include "Scheduler/EventThread.h" #include "Scheduler/LayerHistory.h" -#include "Scheduler/MessageQueue.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncConfiguration.h" #include "Scheduler/VsyncController.h" @@ -263,14 +262,6 @@ bool validateCompositionDataspace(Dataspace dataspace) { return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3; } -class FrameRateFlexibilityToken : public BBinder { -public: - FrameRateFlexibilityToken(std::function<void()> callback) : mCallback(callback) {} - virtual ~FrameRateFlexibilityToken() { mCallback(); } - -private: - std::function<void()> mCallback; -}; enum Permission { ACCESS_SURFACE_FLINGER = 0x1, @@ -339,9 +330,8 @@ Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB; ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888; Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB; ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888; -bool SurfaceFlinger::useFrameRateApi; bool SurfaceFlinger::enableSdrDimming; -bool SurfaceFlinger::enableLatchUnsignaled; +LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig; std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { switch(displayColorSetting) { @@ -371,7 +361,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mTimeStats(std::make_shared<impl::TimeStats>()), mFrameTracer(mFactory.createFrameTracer()), mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())), - mEventQueue(mFactory.createMessageQueue(*this)), mCompositionEngine(mFactory.createCompositionEngine()), mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)), mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()), @@ -494,14 +483,22 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI android::hardware::details::setTrebleTestingOverride(true); } - useFrameRateApi = use_frame_rate_api(true); - mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0); // Debug property overrides ro. property enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false)); - enableLatchUnsignaled = base::GetBoolProperty("debug.sf.latch_unsignaled"s, false); + enableLatchUnsignaledConfig = getLatchUnsignaledConfig(); +} + +LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { + if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) { + return LatchUnsignaledConfig::Always; + } else if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) { + return LatchUnsignaledConfig::Auto; + } else { + return LatchUnsignaledConfig::Disabled; + } } SurfaceFlinger::~SurfaceFlinger() = default; @@ -510,8 +507,8 @@ void SurfaceFlinger::binderDied(const wp<IBinder>&) { // the window manager died on us. prepare its eulogy. mBootFinished = false; - // Sever the link to inputflinger since its gone as well. - static_cast<void>(schedule([=] { mInputFlinger = nullptr; })); + // Sever the link to inputflinger since it's gone as well. + static_cast<void>(mScheduler->schedule([=] { mInputFlinger = nullptr; })); // restore initial conditions (default device unblank, etc) initializeDisplays(); @@ -521,16 +518,7 @@ void SurfaceFlinger::binderDied(const wp<IBinder>&) { } void SurfaceFlinger::run() { - while (true) { - mEventQueue->waitMessage(); - } -} - -template <typename F, typename T> -inline std::future<T> SurfaceFlinger::schedule(F&& f) { - auto [task, future] = makeTask(std::move(f)); - mEventQueue->postMessage(std::move(task)); - return std::move(future); + mScheduler->run(); } sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() { @@ -731,7 +719,7 @@ void SurfaceFlinger::bootFinished() { sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger"))); - static_cast<void>(schedule([=] { + static_cast<void>(mScheduler->schedule([=] { if (input == nullptr) { ALOGE("Failed to link to input service"); } else { @@ -772,7 +760,7 @@ uint32_t SurfaceFlinger::getNewTexture() { if (std::this_thread::get_id() == mMainThreadId) { return genTextures(); } else { - return schedule(genTextures).get(); + return mScheduler->schedule(genTextures).get(); } } @@ -905,9 +893,8 @@ bool SurfaceFlinger::authenticateSurfaceTexture( } bool SurfaceFlinger::authenticateSurfaceTextureLocked( - const sp<IGraphicBufferProducer>& bufferProducer) const { - sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer)); - return mGraphicBufferProducerList.count(surfaceTextureBinder.get()) > 0; + const sp<IGraphicBufferProducer>& /* bufferProducer */) const { + return false; } status_t SurfaceFlinger::getSupportedFrameTimestamps( @@ -1122,7 +1109,7 @@ status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int mode return BAD_VALUE; } - auto future = schedule([=]() -> status_t { + auto future = mScheduler->schedule([=]() -> status_t { const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken)); if (!display) { ALOGE("Attempt to set allowed display modes for invalid display token %p", @@ -1260,7 +1247,7 @@ void SurfaceFlinger::performSetActiveMode() { const auto displayModeAllowed = display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId()); if (!displayModeAllowed) { - desiredActiveModeChangeDone(display); + clearDesiredActiveModeState(display); continue; } @@ -1286,7 +1273,7 @@ void SurfaceFlinger::performSetActiveMode() { } void SurfaceFlinger::disableExpensiveRendering() { - schedule([=]() MAIN_THREAD { + auto future = mScheduler->schedule([=]() MAIN_THREAD { ATRACE_CALL(); if (mPowerAdvisor.isUsingExpensiveRendering()) { const auto& displays = ON_MAIN_THREAD(mDisplays); @@ -1295,7 +1282,9 @@ void SurfaceFlinger::disableExpensiveRendering() { mPowerAdvisor.setExpensiveRenderingExpected(display->getId(), kDisable); } } - }).wait(); + }); + + future.wait(); } std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) { @@ -1334,7 +1323,7 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, Col return BAD_VALUE; } - auto future = schedule([=]() MAIN_THREAD -> status_t { + auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p", @@ -1369,22 +1358,24 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, Col } void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { - static_cast<void>(schedule([=]() MAIN_THREAD { + const char* const whence = __func__; + static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { getHwComposer().setAutoLowLatencyMode(*displayId, on); } else { - ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); + ALOGE("%s: Invalid display token %p", whence, displayToken.get()); } })); } void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) { - static_cast<void>(schedule([=]() MAIN_THREAD { + const char* const whence = __func__; + static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE; getHwComposer().setContentType(*displayId, type); } else { - ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); + ALOGE("%s: Invalid display token %p", whence, displayToken.get()); } })); } @@ -1443,17 +1434,18 @@ status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder> status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken, bool enable, uint8_t componentMask, uint64_t maxFrames) { - return schedule([=]() MAIN_THREAD -> status_t { - if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { - return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable, - componentMask, - maxFrames); - } else { - ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); - return NAME_NOT_FOUND; - } - }) - .get(); + const char* const whence = __func__; + auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t { + if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { + return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable, + componentMask, maxFrames); + } else { + ALOGE("%s: Invalid display token %p", whence, displayToken.get()); + return NAME_NOT_FOUND; + } + }); + + return future.get(); } status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken, @@ -1495,14 +1487,15 @@ status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken, } status_t SurfaceFlinger::enableVSyncInjections(bool enable) { - schedule([=] { + auto future = mScheduler->schedule([=] { Mutex::Autolock lock(mStateLock); if (const auto handle = mScheduler->enableVSyncInjection(enable)) { - mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr); + mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr); } - }).wait(); + }); + future.wait(); return NO_ERROR; } @@ -1518,12 +1511,14 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) { outLayers->clear(); - schedule([=] { + auto future = mScheduler->schedule([=] { const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); mDrawingState.traverseInZOrder([&](Layer* layer) { outLayers->push_back(layer->getLayerDebugInfo(display.get())); }); - }).wait(); + }); + + future.wait(); return NO_ERROR; } @@ -1618,7 +1613,8 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, return BAD_VALUE; } - return ftl::chain(schedule([=]() MAIN_THREAD { + const char* const whence = __func__; + return ftl::chain(mScheduler->schedule([=]() MAIN_THREAD { if (const auto display = getDisplayDeviceLocked(displayToken)) { if (enableSdrDimming) { display->getCompositionDisplay() @@ -1628,7 +1624,7 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, return getHwComposer().setDisplayBrightness(display->getPhysicalId(), brightness.displayBrightness); } else { - ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); + ALOGE("%s: Invalid display token %p", whence, displayToken.get()); return ftl::yield<status_t>(NAME_NOT_FOUND); } })) @@ -1706,7 +1702,7 @@ void SurfaceFlinger::scheduleCommit(FrameHint hint) { mScheduler->resetIdleTimer(); } mPowerAdvisor.notifyDisplayUpdateImminent(); - mEventQueue->scheduleCommit(); + mScheduler->scheduleFrame(); } void SurfaceFlinger::scheduleComposite(FrameHint hint) { @@ -1720,7 +1716,7 @@ void SurfaceFlinger::scheduleRepaint() { } void SurfaceFlinger::scheduleSample() { - static_cast<void>(schedule([this] { sample(); })); + static_cast<void>(mScheduler->schedule([this] { sample(); })); } nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { @@ -1774,24 +1770,6 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { *compositorTiming = getBE().mCompositorTiming; } -void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate, - Scheduler::ModeEvent event) { - const auto display = getDefaultDisplayDeviceLocked(); - if (!display || mBootStage != BootStage::FINISHED) { - return; - } - ATRACE_CALL(); - - // Don't do any updating if the current fps is the same as the new one. - if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) { - ALOGV("Skipping mode %d as it is not part of allowed modes", - refreshRate.getModeId().value()); - return; - } - - setDesiredActiveMode({refreshRate.getMode(), event}); -} - void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) { const bool connected = connection == hal::Connection::CONNECTED; @@ -1833,7 +1811,7 @@ void SurfaceFlinger::setVsyncEnabled(bool enabled) { ATRACE_CALL(); // On main thread to avoid race conditions with display power state. - static_cast<void>(schedule([=]() MAIN_THREAD { + static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD { mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE; if (const auto display = getDefaultDisplayDeviceLocked(); @@ -1950,7 +1928,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // fired yet just wait for the next commit. if (mSetActiveModePending) { if (framePending) { - mEventQueue->scheduleCommit(); + mScheduler->scheduleFrame(); return false; } @@ -1968,7 +1946,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected } if (mTracingEnabledChanged) { - mTracingEnabled = mTracing.isEnabled(); + mTracingEnabled = mLayerTracing.isEnabled(); mTracingEnabledChanged = false; } @@ -1982,24 +1960,37 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // Composite if transactions were committed, or if requested by HWC. bool mustComposite = mMustComposite.exchange(false); { - mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) || - mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) || - mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS); - const bool tracePreComposition = mTracingEnabled && !mTracePostComposition; - ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition); - mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod)); - mustComposite |= flushAndCommitTransactions(); - mustComposite |= latchBuffers(); + bool needsTraversal = false; + if (clearTransactionFlags(eTransactionFlushNeeded)) { + needsTraversal = flushTransactionQueues(); + } - updateLayerGeometry(); + const bool shouldCommit = + (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal; + if (shouldCommit) { + commitTransactions(); + } - if (tracePreComposition) { - if (mVisibleRegionsDirty) { - mTracing.notifyLocked("visibleRegionsDirty"); - } + if (transactionFlushNeeded()) { + setTransactionFlags(eTransactionFlushNeeded); } + + mustComposite |= shouldCommit; + mustComposite |= latchBuffers(); + + // This has to be called after latchBuffers because we want to include the layers that have + // been latched in the commit callback + if (!needsTraversal) { + // Invoke empty transaction callbacks early. + mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */); + } else { + // Invoke OnCommit callbacks. + mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */); + } + + updateLayerGeometry(); } // Layers need to get updated (in the previous line) before we can use them for @@ -2019,34 +2010,6 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER); } -bool SurfaceFlinger::flushAndCommitTransactions() { - ATRACE_CALL(); - - bool needsTraversal = false; - if (clearTransactionFlags(eTransactionFlushNeeded)) { - needsTraversal = flushTransactionQueues(); - } - - const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal; - if (shouldCommit) { - commitTransactions(); - } - - if (!needsTraversal) { - // Invoke empty transaction callbacks early. - mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */); - } else { - // Invoke OnCommit callbacks. - mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */); - } - - if (transactionFlushNeeded()) { - setTransactionFlags(eTransactionFlushNeeded); - } - - return shouldCommit; -} - void SurfaceFlinger::composite(nsecs_t frameTime) { ATRACE_CALL(); @@ -2093,7 +2056,7 @@ void SurfaceFlinger::composite(nsecs_t frameTime) { const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration; refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration; refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime; - refreshArgs.scheduledFrameTime = mEventQueue->getScheduledFrameTime(); + refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); // Store the present time just before calling to the composition engine so we could notify // the scheduler. @@ -2132,12 +2095,12 @@ void SurfaceFlinger::composite(nsecs_t frameTime) { modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition); mLayersWithQueuedFrames.clear(); - if (mTracingEnabled && mTracePostComposition) { - // This may block if SurfaceTracing is running in sync mode. + if (mTracingEnabled) { + // This will block and should only be used for debugging. if (mVisibleRegionsDirty) { - mTracing.notify("visibleRegionsDirty"); - } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) { - mTracing.notify("bufferLatched"); + mLayerTracing.notify("visibleRegionsDirty"); + } else if (mLayerTracing.flagIsSet(LayerTracing::TRACE_BUFFERS)) { + mLayerTracing.notify("bufferLatched"); } } @@ -2428,7 +2391,7 @@ void SurfaceFlinger::postComposition() { } } -void SurfaceFlinger::computeLayerBounds() { +FloatRect SurfaceFlinger::getMaxDisplayBounds() { // Find the largest width and height among all the displays. int32_t maxDisplayWidth = 0; int32_t maxDisplayHeight = 0; @@ -2446,8 +2409,13 @@ void SurfaceFlinger::computeLayerBounds() { // 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(-maxDisplayWidth * 10, -maxDisplayHeight * 10, maxDisplayWidth * 10, - maxDisplayHeight * 10); + 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 */); } @@ -2815,7 +2783,7 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { mDisplays.erase(displayToken); if (display && display->isVirtual()) { - static_cast<void>(schedule([display = std::move(display)] { + static_cast<void>(mScheduler->schedule([display = std::move(display)] { // Destroy the display without holding the mStateLock. // This is a temporary solution until we can manage transaction queues without // holding the mStateLock. @@ -3055,50 +3023,47 @@ void SurfaceFlinger::updateInputFlinger() { mInputWindowCommands.clear(); } -bool enablePerWindowInputRotation() { - static bool value = - android::base::GetBoolProperty("persist.debug.per_window_input_rotation", true); - return value; -} - void SurfaceFlinger::notifyWindowInfos() { std::vector<WindowInfo> windowInfos; std::vector<DisplayInfo> displayInfos; - std::unordered_map<uint32_t /*layerStackId*/, const ui::Transform> displayTransforms; + std::unordered_map<uint32_t /*layerStackId*/, + std::pair<bool /* isSecure */, const ui::Transform>> + inputDisplayDetails; - if (enablePerWindowInputRotation()) { - for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) { - if (!display->receivesInput()) { - continue; - } - const uint32_t layerStackId = display->getLayerStack().id; - const auto& [info, transform] = display->getInputInfo(); - const auto& [it, emplaced] = displayTransforms.try_emplace(layerStackId, transform); - if (!emplaced) { - ALOGE("Multiple displays claim to accept input for the same layer stack: %u", - layerStackId); - continue; - } - displayInfos.emplace_back(info); + for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) { + if (!display->receivesInput()) { + continue; } + const uint32_t layerStackId = display->getLayerStack().id; + const auto& [info, transform] = display->getInputInfo(); + const auto& [it, emplaced] = + inputDisplayDetails.try_emplace(layerStackId, display->isSecure(), transform); + if (!emplaced) { + ALOGE("Multiple displays claim to accept input for the same layer stack: %u", + layerStackId); + continue; + } + displayInfos.emplace_back(info); } mDrawingState.traverseInReverseZOrder([&](Layer* layer) { if (!layer->needsInputInfo()) return; - const DisplayDevice* display = ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)).get(); + bool isSecure = true; ui::Transform displayTransform = ui::Transform(); - if (enablePerWindowInputRotation() && display != nullptr) { - // When calculating the screen bounds we ignore the transparent region since it may - // result in an unwanted offset. - const auto it = displayTransforms.find(display->getLayerStack().id); - if (it != displayTransforms.end()) { - displayTransform = it->second; - } + const uint32_t layerStackId = layer->getLayerStack().id; + const auto it = inputDisplayDetails.find(layerStackId); + if (it != inputDisplayDetails.end()) { + const auto& [secure, transform] = it->second; + isSecure = secure; + displayTransform = transform; + } else { + ALOGE("No input-enabled display found for layer `%s` on layer stack id: %d", + layer->getDebugName(), layerStackId); } - const bool displayIsSecure = !display || display->isSecure(); - windowInfos.push_back(layer->fillInputInfo(displayTransform, displayIsSecure)); + + windowInfos.push_back(layer->fillInputInfo(displayTransform, isSecure)); }); mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, mInputWindowCommands.syncInputWindows); @@ -3121,7 +3086,21 @@ void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, Scheduler // Scheduler::chooseRefreshRateForContent ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); - changeRefreshRateLocked(refreshRate, event); + + const auto display = getDefaultDisplayDeviceLocked(); + if (!display || mBootStage != BootStage::FINISHED) { + return; + } + ATRACE_CALL(); + + // Don't do any updating if the current fps is the same as the new one. + if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) { + ALOGV("Skipping mode %d as it is not part of allowed modes", + refreshRate.getModeId().value()); + return; + } + + setDesiredActiveMode({refreshRate.getMode(), event}); } void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { @@ -3149,8 +3128,20 @@ void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate); mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs()); - // start the EventThread - mScheduler = getFactory().createScheduler(display->holdRefreshRateConfigs(), *this); + const Scheduler::Options options = { + .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}; + + mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), + static_cast<ISchedulerCallback&>(*this), options); + { + auto configs = display->holdRefreshRateConfigs(); + mScheduler->createVsyncSchedule(configs->supportsKernelIdleTimer()); + mScheduler->setRefreshRateConfigs(std::move(configs)); + } + + setVsyncEnabled(false); + mScheduler->startTimers(); + const auto configs = mVsyncConfiguration->getCurrentConfigs(); const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs(); mAppConnectionHandle = @@ -3166,8 +3157,8 @@ void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { mInterceptor->saveVSyncEvent(timestamp); }); - mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(), - configs.late.sfWorkDuration); + mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(), + configs.late.sfWorkDuration); mRegionSamplingThread = new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables()); @@ -3201,7 +3192,7 @@ void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config, mScheduler->setDuration(mSfConnectionHandle, /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod), /*readyDuration=*/config.sfWorkDuration); - mEventQueue->setDuration(config.sfWorkDuration); + mScheduler->setDuration(config.sfWorkDuration); } void SurfaceFlinger::doCommitTransactions() { @@ -3355,20 +3346,15 @@ bool SurfaceFlinger::latchBuffers() { } status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc, - const wp<Layer>& parent, bool addToRoot, - uint32_t* outTransformHint) { + const sp<Layer>& lbc, const wp<Layer>& parent, + bool addToRoot, uint32_t* outTransformHint) { if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) { ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), ISurfaceComposer::MAX_LAYERS); return NO_MEMORY; } - wp<IBinder> initialProducer; - if (gbc != nullptr) { - initialProducer = IInterface::asBinder(gbc); - } - setLayerCreatedState(handle, lbc, parent, initialProducer, addToRoot); + setLayerCreatedState(handle, lbc, parent, addToRoot); // Create a transaction includes the initial parent and producer. Vector<ComposerState> states; @@ -3384,7 +3370,9 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind *outTransformHint = mActiveDisplayTransformHint; } // attach this layer to the client - client->attachLayer(handle, lbc); + if (client != nullptr) { + client->attachLayer(handle, lbc); + } return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr, InputWindowCommands{}, -1 /* desiredPresentTime */, @@ -3392,13 +3380,6 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind 0 /* Undefined transactionId */); } -void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) { - static_cast<void>(schedule([=] { - Mutex::Autolock lock(mStateLock); - mGraphicBufferProducerList.erase(binder); - })); -} - uint32_t SurfaceFlinger::getTransactionFlags() const { return mTransactionFlags; } @@ -3420,29 +3401,34 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule } bool SurfaceFlinger::flushTransactionQueues() { - bool needsTraversal = false; // to prevent onHandleDestroyed from being called while the lock is held, // we must keep a copy of the transactions (specifically the composer // states) around outside the scope of the lock - std::vector<const TransactionState> transactions; + std::vector<TransactionState> transactions; // Layer handles that have transactions with buffers that are ready to be applied. std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent; { Mutex::Autolock _l(mStateLock); { Mutex::Autolock _l(mQueueLock); + // allowLatchUnsignaled acts as a filter condition when latch unsignaled is either auto + // or always. auto: in this case we let buffer latch unsignaled if we have only one + // applyToken and if only first transaction is latch unsignaled. If more than one + // applyToken we don't latch unsignaled. + bool allowLatchUnsignaled = allowedLatchUnsignaled(); + bool isFirstUnsignaledTransactionApplied = false; // Collect transactions from pending transaction queue. auto it = mPendingTransactionQueues.begin(); while (it != mPendingTransactionQueues.end()) { auto& [applyToken, transactionQueue] = *it; - while (!transactionQueue.empty()) { auto& transaction = transactionQueue.front(); if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo, transaction.isAutoTimestamp, transaction.desiredPresentTime, transaction.originUid, transaction.states, - bufferLayersReadyToPresent)) { + bufferLayersReadyToPresent, + allowLatchUnsignaled)) { setTransactionFlags(eTransactionFlushNeeded); break; } @@ -3451,6 +3437,14 @@ bool SurfaceFlinger::flushTransactionQueues() { }); transactions.emplace_back(std::move(transaction)); transactionQueue.pop(); + if (allowLatchUnsignaled && + enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) { + // if allowLatchUnsignaled && we are in LatchUnsignaledConfig::Auto + // then we should have only one applyToken for processing. + // so we can stop further transactions on this applyToken. + isFirstUnsignaledTransactionApplied = true; + break; + } } if (transactionQueue.empty()) { @@ -3462,52 +3456,118 @@ bool SurfaceFlinger::flushTransactionQueues() { } // Collect transactions from current transaction queue or queue to pending transactions. - // Case 1: push to pending when transactionIsReadyToBeApplied is false. + // Case 1: push to pending when transactionIsReadyToBeApplied is false + // or the first transaction was unsignaled. // Case 2: push to pending when there exist a pending queue. - // Case 3: others are ready to apply. + // Case 3: others are the transactions that are ready to apply. while (!mTransactionQueue.empty()) { auto& transaction = mTransactionQueue.front(); bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) != mPendingTransactionQueues.end(); - if (pendingTransactions || + if (isFirstUnsignaledTransactionApplied || pendingTransactions || !transactionIsReadyToBeApplied(transaction.frameTimelineInfo, transaction.isAutoTimestamp, transaction.desiredPresentTime, transaction.originUid, transaction.states, - bufferLayersReadyToPresent)) { + bufferLayersReadyToPresent, + allowLatchUnsignaled)) { mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction)); } else { transaction.traverseStatesWithBuffers([&](const layer_state_t& state) { bufferLayersReadyToPresent.insert(state.surface); }); transactions.emplace_back(std::move(transaction)); + if (allowLatchUnsignaled && + enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) { + isFirstUnsignaledTransactionApplied = true; + } } - mTransactionQueue.pop(); + mTransactionQueue.pop_front(); ATRACE_INT("TransactionQueue", mTransactionQueue.size()); } + + return applyTransactions(transactions); } + } +} - // Now apply all transactions. - for (const auto& transaction : transactions) { - needsTraversal |= - applyTransactionState(transaction.frameTimelineInfo, transaction.states, - transaction.displays, transaction.flags, - transaction.inputWindowCommands, - transaction.desiredPresentTime, - transaction.isAutoTimestamp, transaction.buffer, - transaction.postTime, transaction.permissions, - transaction.hasListenerCallbacks, - transaction.listenerCallbacks, transaction.originPid, - transaction.originUid, transaction.id); - if (transaction.transactionCommittedSignal) { - mTransactionCommittedSignals.emplace_back( - std::move(transaction.transactionCommittedSignal)); - } +bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) { + bool needsTraversal = false; + // Now apply all transactions. + for (const auto& transaction : transactions) { + needsTraversal |= + applyTransactionState(transaction.frameTimelineInfo, transaction.states, + transaction.displays, transaction.flags, + transaction.inputWindowCommands, + transaction.desiredPresentTime, transaction.isAutoTimestamp, + transaction.buffer, transaction.postTime, + transaction.permissions, transaction.hasListenerCallbacks, + transaction.listenerCallbacks, transaction.originPid, + transaction.originUid, transaction.id); + if (transaction.transactionCommittedSignal) { + mTransactionCommittedSignals.emplace_back( + std::move(transaction.transactionCommittedSignal)); } - } // unlock mStateLock + } return needsTraversal; } +bool SurfaceFlinger::allowedLatchUnsignaled() { + if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) { + return false; + } + // Always mode matches the current latch unsignaled behavior. + // This behavior is currently used by the partners and we would like + // to keep it until we are completely migrated to Auto mode successfully + // and we we have our fallback based implementation in place. + if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) { + return true; + } + + // if enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto + // we don't latch unsignaled if more than one applyToken, as it can backpressure + // the other transactions. + if (mPendingTransactionQueues.size() > 1) { + return false; + } + std::optional<sp<IBinder>> applyToken = std::nullopt; + bool isPendingTransactionQueuesItem = false; + if (!mPendingTransactionQueues.empty()) { + applyToken = mPendingTransactionQueues.begin()->first; + isPendingTransactionQueuesItem = true; + } + + for (const auto& item : mTransactionQueue) { + if (!applyToken.has_value()) { + applyToken = item.applyToken; + } else if (applyToken.has_value() && applyToken != item.applyToken) { + return false; + } + } + + if (isPendingTransactionQueuesItem) { + return checkTransactionCanLatchUnsignaled( + mPendingTransactionQueues.begin()->second.front()); + } else if (applyToken.has_value()) { + return checkTransactionCanLatchUnsignaled((mTransactionQueue.front())); + } + return false; +} + +bool SurfaceFlinger::checkTransactionCanLatchUnsignaled(const TransactionState& transaction) { + if (transaction.states.size() == 1) { + const auto& state = transaction.states.begin()->state; + if ((state.flags & ~layer_state_t::eBufferChanged) == 0 && + state.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) && + state.bufferData.acquireFence && + state.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) { + ATRACE_NAME("transactionCanLatchUnsignaled"); + return true; + } + } + return false; +} + bool SurfaceFlinger::transactionFlushNeeded() { Mutex::Autolock _l(mQueueLock); return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty(); @@ -3539,7 +3599,8 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied( const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, uid_t originUid, const Vector<ComposerState>& states, const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& - bufferLayersReadyToPresent) const { + bufferLayersReadyToPresent, + bool allowLatchUnsignaled) const { ATRACE_CALL(); const nsecs_t expectedPresentTime = mExpectedPresentTime.load(); // Do not present if the desiredPresentTime has not passed unless it is more than one second @@ -3566,7 +3627,7 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied( const layer_state_t& s = state.state; const bool acquireFenceChanged = s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged); - if (acquireFenceChanged && s.bufferData.acquireFence && !enableLatchUnsignaled && + if (acquireFenceChanged && s.bufferData.acquireFence && !allowLatchUnsignaled && s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) { ATRACE_NAME("fence unsignaled"); return false; @@ -3627,7 +3688,7 @@ void SurfaceFlinger::queueTransaction(TransactionState& state) { : CountDownLatch::eSyncTransaction)); } - mTransactionQueue.emplace(state); + mTransactionQueue.emplace_back(state); ATRACE_INT("TransactionQueue", mTransactionQueue.size()); const auto schedule = [](uint32_t flags) { @@ -4207,17 +4268,14 @@ status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder> sp<Layer> mirrorLayer; sp<Layer> mirrorFrom; - std::string layerName = "MirrorRoot"; - { Mutex::Autolock _l(mStateLock); mirrorFrom = fromHandle(mirrorFromHandle).promote(); if (!mirrorFrom) { return NAME_NOT_FOUND; } - - status_t result = createContainerLayer(client, std::move(layerName), -1, -1, 0, - LayerMetadata(), outHandle, &mirrorLayer); + LayerCreationArgs args(this, client, "MirrorRoot", 0, LayerMetadata()); + status_t result = createContainerLayer(args, outHandle, &mirrorLayer); if (result != NO_ERROR) { return result; } @@ -4226,22 +4284,13 @@ status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder> } *outLayerId = mirrorLayer->sequence; - return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, false, - nullptr /* outTransformHint */); + return addClientLayer(client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */, + false /* addAsRoot */, nullptr /* outTransformHint */); } -status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w, - uint32_t h, PixelFormat format, uint32_t flags, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, +status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, const sp<IBinder>& parentHandle, int32_t* outLayerId, const sp<Layer>& parentLayer, uint32_t* outTransformHint) { - if (int32_t(w|h) < 0) { - ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)", - int(w), int(h)); - return BAD_VALUE; - } - ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr, "Expected only one of parentLayer or parentHandle to be non-null. " "Programmer error?"); @@ -4250,40 +4299,22 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie sp<Layer> layer; - std::string layerName{name.string()}; - - switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { + switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: case ISurfaceComposerClient::eFXSurfaceBufferState: { - result = createBufferStateLayer(client, std::move(layerName), w, h, flags, - std::move(metadata), handle, &layer); + result = createBufferStateLayer(args, outHandle, &layer); std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter(); if (pendingBufferCounter) { std::string counterName = layer->getPendingBufferCounterName(); - mBufferCountTracker.add((*handle)->localBinder(), counterName, + mBufferCountTracker.add((*outHandle)->localBinder(), counterName, pendingBufferCounter); } } break; case ISurfaceComposerClient::eFXSurfaceEffect: - // check if buffer size is set for color layer. - if (w > 0 || h > 0) { - ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)", - int(w), int(h)); - return BAD_VALUE; - } - - result = createEffectLayer(client, std::move(layerName), w, h, flags, - std::move(metadata), handle, &layer); + result = createEffectLayer(args, outHandle, &layer); break; case ISurfaceComposerClient::eFXSurfaceContainer: - // check if buffer size is set for container layer. - if (w > 0 || h > 0) { - ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)", - int(w), int(h)); - return BAD_VALUE; - } - result = createContainerLayer(client, std::move(layerName), w, h, flags, - std::move(metadata), handle, &layer); + result = createContainerLayer(args, outHandle, &layer); break; default: result = BAD_VALUE; @@ -4303,7 +4334,7 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie if (parentLayer != nullptr) { addToRoot = false; } - result = addClientLayer(client, *handle, *gbp, layer, parent, addToRoot, outTransformHint); + result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint); if (result != NO_ERROR) { return result; } @@ -4313,9 +4344,7 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return result; } -status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::string name, - uint32_t w, uint32_t h, uint32_t flags, - LayerMetadata metadata, PixelFormat& format, +status_t SurfaceFlinger::createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) { @@ -4331,7 +4360,6 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::s } sp<BufferQueueLayer> layer; - LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata)); args.textureName = getNewTexture(); { // Grab the SF state lock during this since it's the only safe way to access @@ -4341,7 +4369,7 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::s layer = getFactory().createBufferQueueLayer(args); } - status_t err = layer->setDefaultBufferProperties(w, h, format); + status_t err = layer->setDefaultBufferProperties(0, 0, format); if (err == NO_ERROR) { *handle = layer->getHandle(); *gbp = layer->getProducer(); @@ -4352,34 +4380,24 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::s return err; } -status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name, - uint32_t w, uint32_t h, uint32_t flags, - LayerMetadata metadata, sp<IBinder>* handle, +status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle, sp<Layer>* outLayer) { - LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata)); args.textureName = getNewTexture(); - sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args); - *handle = layer->getHandle(); - *outLayer = layer; - + *outLayer = getFactory().createBufferStateLayer(args); + *handle = (*outLayer)->getHandle(); return NO_ERROR; } -status_t SurfaceFlinger::createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, - uint32_t h, uint32_t flags, LayerMetadata metadata, - sp<IBinder>* handle, sp<Layer>* outLayer) { - *outLayer = getFactory().createEffectLayer( - {this, client, std::move(name), w, h, flags, std::move(metadata)}); +status_t SurfaceFlinger::createEffectLayer(LayerCreationArgs& args, sp<IBinder>* handle, + sp<Layer>* outLayer) { + *outLayer = getFactory().createEffectLayer(args); *handle = (*outLayer)->getHandle(); return NO_ERROR; } -status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, std::string name, - uint32_t w, uint32_t h, uint32_t flags, - LayerMetadata metadata, sp<IBinder>* handle, +status_t SurfaceFlinger::createContainerLayer(LayerCreationArgs& args, sp<IBinder>* handle, sp<Layer>* outLayer) { - *outLayer = getFactory().createContainerLayer( - {this, client, std::move(name), w, h, flags, std::move(metadata)}); + *outLayer = getFactory().createContainerLayer(args); *handle = (*outLayer)->getHandle(); return NO_ERROR; } @@ -4448,26 +4466,7 @@ void SurfaceFlinger::onInitializeDisplays() { void SurfaceFlinger::initializeDisplays() { // Async since we may be called from the main thread. - static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); })); -} - -sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const { - const auto filter = layer->getOutputFilter(); - sp<DisplayDevice> inputDisplay; - - for (const auto& [_, display] : mDisplays) { - if (!display->receivesInput() || !display->getCompositionDisplay()->includesLayer(filter)) { - continue; - } - // Don't return immediately so that we can log duplicates. - if (inputDisplay) { - ALOGE("Multiple displays claim to accept input for the same layer stack: %u", - filter.layerStack.id); - continue; - } - inputDisplay = display; - } - return inputDisplay; + static_cast<void>(mScheduler->schedule([this]() MAIN_THREAD { onInitializeDisplays(); })); } void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { @@ -4567,7 +4566,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { - schedule([=]() MAIN_THREAD { + auto future = mScheduler->schedule([=]() MAIN_THREAD { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { ALOGE("Attempt to set power mode %d for invalid display token %p", mode, @@ -4577,7 +4576,9 @@ void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { } else { setPowerModeInternal(display, static_cast<hal::PowerMode>(mode)); } - }).wait(); + }); + + future.wait(); } status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { @@ -4627,7 +4628,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { } if (dumpLayers) { - LayersTraceFileProto traceFileProto = SurfaceTracing::createLayersTraceFileProto(); + LayersTraceFileProto traceFileProto = mLayerTracing.createTraceFileProto(); LayersTraceProto* layersTrace = traceFileProto.add_entry(); LayersProto layersProto = dumpProtoFromMainThread(); layersTrace->mutable_layers()->Swap(&layersProto); @@ -4649,8 +4650,8 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { } status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { - if (asProto && mTracing.isEnabled()) { - mTracing.writeToFile(); + if (asProto && mLayerTracing.isEnabled()) { + mLayerTracing.writeToFile(); } return doDump(fd, DumpArgs(), asProto); @@ -4899,21 +4900,21 @@ void SurfaceFlinger::dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t } LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) { - return schedule([=] { return dumpDrawingStateProto(traceFlags); }).get(); + return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get(); } void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { + auto future = mScheduler->schedule([this] { + std::string result; + for (Layer* offscreenLayer : mOffscreenLayers) { + offscreenLayer->traverse(LayerVector::StateSet::Drawing, + [&](Layer* layer) { layer->dumpCallingUidPid(result); }); + } + return result; + }); + result.append("Offscreen Layers:\n"); - result.append(schedule([this] { - std::string result; - for (Layer* offscreenLayer : mOffscreenLayers) { - offscreenLayer->traverse(LayerVector::StateSet::Drawing, - [&](Layer* layer) { - layer->dumpCallingUidPid(result); - }); - } - return result; - }).get()); + result.append(future.get()); } void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const { @@ -4965,8 +4966,6 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co */ colorizer.bold(result); StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load()); - StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n", - mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize); colorizer.reset(result); { @@ -5048,7 +5047,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co /* * Tracing state */ - mTracing.dump(result); + mLayerTracing.dump(result); result.append("\n"); /* @@ -5160,11 +5159,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case SET_GLOBAL_SHADOW_SETTINGS: case GET_PRIMARY_PHYSICAL_DISPLAY_ID: case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: { - // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests, - // which acquire the necessary permission dynamically. Don't use the permission cache - // for this check. - bool usePermissionCache = - code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES; + // OVERRIDE_HDR_TYPES is used by CTS tests, which acquire the necessary + // permission dynamically. Don't use the permission cache for this check. + bool usePermissionCache = code != OVERRIDE_HDR_TYPES; if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) { IPCThreadState* ipc = IPCThreadState::self(); ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", @@ -5327,6 +5324,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r scheduleRepaint(); return NO_ERROR; case 1004: // Force composite ahead of next VSYNC. + case 1006: scheduleComposite(FrameHint::kActive); return NO_ERROR; case 1005: { // Force commit ahead of next VSYNC. @@ -5335,9 +5333,6 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r eTraversalNeeded); return NO_ERROR; } - case 1006: // Force composite immediately. - mEventQueue->scheduleComposite(); - return NO_ERROR; case 1007: // Unused. return NAME_NOT_FOUND; case 1008: // Toggle forced GPU composition. @@ -5448,7 +5443,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } case 1021: { // Disable HWC virtual displays const bool enable = data.readInt32() != 0; - static_cast<void>(schedule([this, enable] { enableHalVirtualDisplays(enable); })); + static_cast<void>( + mScheduler->schedule([this, enable] { enableHalVirtualDisplays(enable); })); return NO_ERROR; } case 1022: { // Set saturation boost @@ -5477,20 +5473,21 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r bool tracingEnabledChanged; if (n) { ALOGD("LayerTracing enabled"); - tracingEnabledChanged = mTracing.enable(); + tracingEnabledChanged = mLayerTracing.enable(); if (tracingEnabledChanged) { - schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait(); + mScheduler->schedule([&]() MAIN_THREAD { mLayerTracing.notify("start"); }) + .wait(); } } else { ALOGD("LayerTracing disabled"); - tracingEnabledChanged = mTracing.disable(); + tracingEnabledChanged = mLayerTracing.disable(); } mTracingEnabledChanged = tracingEnabledChanged; reply->writeInt32(NO_ERROR); return NO_ERROR; } case 1026: { // Get layer tracing status - reply->writeBool(mTracing.isEnabled()); + reply->writeBool(mLayerTracing.isEnabled()); return NO_ERROR; } // Is a DisplayColorSetting supported? @@ -5531,7 +5528,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } ALOGD("Updating trace buffer to %d KB", n); - mTracing.setBufferSize(n * 1024); + mLayerTracing.setBufferSize(n * 1024); reply->writeInt32(NO_ERROR); return NO_ERROR; } @@ -5576,12 +5573,12 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1033: { n = data.readUint32(); ALOGD("Updating trace flags to 0x%x", n); - mTracing.setTraceFlags(n); + mLayerTracing.setTraceFlags(n); reply->writeInt32(NO_ERROR); return NO_ERROR; } case 1034: { - schedule([&] { + auto future = mScheduler->schedule([&] { switch (n = data.readInt32()) { case 0: case 1: @@ -5591,7 +5588,9 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r reply->writeBool(ON_MAIN_THREAD(isRefreshRateOverlayEnabled())); } } - }).get(); + }); + + future.wait(); return NO_ERROR; } case 1035: { @@ -5616,17 +5615,43 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mDebugDisplayModeSetByBackdoor = result == NO_ERROR; return result; } + // Turn on/off frame rate flexibility mode. When turned on it overrides the display + // manager frame rate policy a new policy which allows switching between all refresh + // rates. case 1036: { - if (data.readInt32() > 0) { - status_t result = - acquireFrameRateFlexibilityToken(&mDebugFrameRateFlexibilityToken); - if (result != NO_ERROR) { - return result; - } - } else { - mDebugFrameRateFlexibilityToken = nullptr; + if (data.readInt32() > 0) { // turn on + return mScheduler + ->schedule([this] { + const auto display = + ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); + + // This is a little racy, but not in a way that hurts anything. As + // we grab the defaultMode from the display manager policy, we could + // be setting a new display manager policy, leaving us using a stale + // defaultMode. The defaultMode doesn't matter for the override + // policy though, since we set allowGroupSwitching to true, so it's + // not a problem. + scheduler::RefreshRateConfigs::Policy overridePolicy; + overridePolicy.defaultMode = display->refreshRateConfigs() + .getDisplayManagerPolicy() + .defaultMode; + overridePolicy.allowGroupSwitching = true; + constexpr bool kOverridePolicy = true; + return setDesiredDisplayModeSpecsInternal(display, overridePolicy, + kOverridePolicy); + }) + .get(); + } else { // turn off + return mScheduler + ->schedule([this] { + const auto display = + ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); + constexpr bool kOverridePolicy = true; + return setDesiredDisplayModeSpecsInternal(display, {}, + kOverridePolicy); + }) + .get(); } - return NO_ERROR; } // Inject a hotplug connected event for the primary display. This will deallocate and // reallocate the display state including framebuffers. @@ -5669,31 +5694,29 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r // Second argument is an optional uint64 - if present, then limits enabling/disabling // caching to a particular physical display case 1040: { - status_t error = - schedule([&] { - n = data.readInt32(); - std::optional<PhysicalDisplayId> inputId = std::nullopt; - if (uint64_t inputDisplayId; - data.readUint64(&inputDisplayId) == NO_ERROR) { - inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId); - if (!inputId || getPhysicalDisplayToken(*inputId)) { - ALOGE("No display with id: %" PRIu64, inputDisplayId); - return NAME_NOT_FOUND; - } - } - { - Mutex::Autolock lock(mStateLock); - mLayerCachingEnabled = n != 0; - for (const auto& [_, display] : mDisplays) { - if (!inputId || *inputId == display->getPhysicalId()) { - display->enableLayerCaching(mLayerCachingEnabled); - } - } + auto future = mScheduler->schedule([&] { + n = data.readInt32(); + std::optional<PhysicalDisplayId> inputId = std::nullopt; + if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) { + inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId); + if (!inputId || getPhysicalDisplayToken(*inputId)) { + ALOGE("No display with id: %" PRIu64, inputDisplayId); + return NAME_NOT_FOUND; + } + } + { + Mutex::Autolock lock(mStateLock); + mLayerCachingEnabled = n != 0; + for (const auto& [_, display] : mDisplays) { + if (!inputId || *inputId == display->getPhysicalId()) { + display->enableLayerCaching(mLayerCachingEnabled); } - return OK; - }).get(); + } + } + return OK; + }); - if (error != OK) { + if (const status_t error = future.get(); error != OK) { return error; } scheduleRepaint(); @@ -5712,7 +5735,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { // Update the overlay on the main thread to avoid race conditions with // mRefreshRateConfigs->getCurrentRefreshRate() - static_cast<void>(schedule([=] { + static_cast<void>(mScheduler->schedule([=] { const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); if (!display) { ALOGW("%s: default display is null", __func__); @@ -5727,7 +5750,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { const bool timerExpired = mKernelIdleTimerEnabled && expired; if (display->onKernelTimerChanged(desiredModeId, timerExpired)) { - mEventQueue->scheduleCommit(); + mScheduler->scheduleFrame(); } })); } @@ -5984,9 +6007,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(); @@ -5995,7 +6016,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; } @@ -6034,43 +6055,38 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } } - const auto display = - findDisplay([layerStack = parent->getLayerStack()](const auto& display) { - return display.getLayerStack() == layerStack; - }); - - 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([layerStack = parent->getLayerStack()](const auto& display) { + return display.getLayerStack() == layerStack; + }); + if (!display) { + // If the layer is not on a display, use the dataspace for the default display. + display = getDefaultDisplayDeviceLocked(); + } + const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode; dataspace = pickDataspaceFromColorMode(colorMode); } - 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) { @@ -6126,14 +6142,15 @@ std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScre const bool supportsProtected = getRenderEngine().supportsProtectedContent(); bool hasProtectedLayer = false; if (allowProtected && supportsProtected) { - hasProtectedLayer = schedule([=]() { - bool protectedLayerFound = false; - traverseLayers([&](Layer* layer) { - protectedLayerFound = protectedLayerFound || - (layer->isVisible() && layer->isProtected()); - }); - return protectedLayerFound; - }).get(); + auto future = mScheduler->schedule([=]() { + bool protectedLayerFound = false; + traverseLayers([&](Layer* layer) { + protectedLayerFound = + protectedLayerFound || (layer->isVisible() && layer->isProtected()); + }); + return protectedLayerFound; + }); + hasProtectedLayer = future.get(); } const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | @@ -6164,9 +6181,11 @@ std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScre bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission(); - auto scheduleResultFuture = schedule([=, - renderAreaFuture = std::move(renderAreaFuture)]() mutable - -> std::shared_future<renderengine::RenderEngineResult> { + auto scheduleResultFuture = mScheduler->schedule([=, + renderAreaFuture = + std::move(renderAreaFuture)]() mutable + -> std::shared_future< + renderengine::RenderEngineResult> { ScreenCaptureResults captureResults; std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get(); if (!renderArea) { @@ -6459,7 +6478,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs( return BAD_VALUE; } - auto future = schedule([=]() -> status_t { + auto future = mScheduler->schedule([=]() -> status_t { const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken)); if (!display) { ALOGE("Attempt to set desired display modes for invalid display token %p", @@ -6596,7 +6615,7 @@ status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, return BAD_VALUE; } - static_cast<void>(schedule([=] { + static_cast<void>(mScheduler->schedule([=] { Mutex::Autolock lock(mStateLock); if (authenticateSurfaceTextureLocked(surface)) { sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer(); @@ -6622,74 +6641,6 @@ status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, return NO_ERROR; } -status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) { - if (!outToken) { - return BAD_VALUE; - } - - auto future = schedule([this] { - status_t result = NO_ERROR; - sp<IBinder> token; - - if (mFrameRateFlexibilityTokenCount == 0) { - const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); - - // This is a little racy, but not in a way that hurts anything. As we grab the - // defaultMode from the display manager policy, we could be setting a new display - // manager policy, leaving us using a stale defaultMode. The defaultMode doesn't - // matter for the override policy though, since we set allowGroupSwitching to - // true, so it's not a problem. - scheduler::RefreshRateConfigs::Policy overridePolicy; - overridePolicy.defaultMode = - display->refreshRateConfigs().getDisplayManagerPolicy().defaultMode; - overridePolicy.allowGroupSwitching = true; - constexpr bool kOverridePolicy = true; - result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy); - } - - if (result == NO_ERROR) { - mFrameRateFlexibilityTokenCount++; - // Handing out a reference to the SurfaceFlinger object, as we're doing in the line - // below, is something to consider carefully. The lifetime of the - // FrameRateFlexibilityToken isn't tied to SurfaceFlinger object lifetime, so if this - // SurfaceFlinger object were to be destroyed while the token still exists, the token - // destructor would be accessing a stale SurfaceFlinger reference, and crash. This is ok - // in this case, for two reasons: - // 1. Once SurfaceFlinger::run() is called by main_surfaceflinger.cpp, the only way - // the program exits is via a crash. So we won't have a situation where the - // SurfaceFlinger object is dead but the process is still up. - // 2. The frame rate flexibility token is acquired/released only by CTS tests, so even - // if condition 1 were changed, the problem would only show up when running CTS tests, - // not on end user devices, so we could spot it and fix it without serious impact. - token = new FrameRateFlexibilityToken( - [this]() { onFrameRateFlexibilityTokenReleased(); }); - ALOGD("Frame rate flexibility token acquired. count=%d", - mFrameRateFlexibilityTokenCount); - } - - return std::make_pair(result, token); - }); - - status_t result; - std::tie(result, *outToken) = future.get(); - return result; -} - -void SurfaceFlinger::onFrameRateFlexibilityTokenReleased() { - static_cast<void>(schedule([this] { - LOG_ALWAYS_FATAL_IF(mFrameRateFlexibilityTokenCount == 0, - "Failed tracking frame rate flexibility tokens"); - mFrameRateFlexibilityTokenCount--; - ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount); - if (mFrameRateFlexibilityTokenCount == 0) { - const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); - constexpr bool kOverridePolicy = true; - status_t result = setDesiredDisplayModeSpecsInternal(display, {}, kOverridePolicy); - LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token"); - } - })); -} - status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, const FrameTimelineInfo& frameTimelineInfo) { Mutex::Autolock lock(mStateLock); @@ -6783,11 +6734,10 @@ void TransactionState::traverseStatesWithBuffers( } void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer, - const wp<Layer> parent, const wp<IBinder>& producer, - bool addToRoot) { + const wp<Layer> parent, bool addToRoot) { Mutex::Autolock lock(mCreatedLayersLock); mCreatedLayers[handle->localBinder()] = - std::make_unique<LayerCreatedState>(layer, parent, producer, addToRoot); + std::make_unique<LayerCreatedState>(layer, parent, addToRoot); } auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) { @@ -6825,14 +6775,16 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) { } sp<Layer> parent; + bool addToRoot = state->addToRoot; if (state->initialParent != nullptr) { parent = state->initialParent.promote(); if (parent == nullptr) { ALOGE("Invalid parent %p", state->initialParent.unsafe_get()); + addToRoot = false; } } - if (parent == nullptr && state->addToRoot) { + if (parent == nullptr && addToRoot) { layer->setIsAtRoot(true); mCurrentState.layersSortedByZ.add(layer); } else if (parent == nullptr) { @@ -6846,19 +6798,6 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) { layer->updateTransformHint(mActiveDisplayTransformHint); - if (state->initialProducer != nullptr) { - mGraphicBufferProducerList.insert(state->initialProducer); - LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize, - "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers", - mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize, - mNumLayers.load()); - if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) { - ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers", - mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize, - mNumLayers.load()); - } - } - mInterceptor->saveSurfaceCreation(layer); return layer; } @@ -6868,7 +6807,7 @@ void SurfaceFlinger::sample() { return; } - mRegionSamplingThread->onCompositionComplete(mEventQueue->getScheduledFrameTime()); + mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime()); } void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index a1e431b54f..6093be91f9 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -56,14 +56,13 @@ #include "Fps.h" #include "FrameTracker.h" #include "LayerVector.h" -#include "Scheduler/MessageQueue.h" #include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/RefreshRateStats.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncModulator.h" #include "SurfaceFlingerFactory.h" -#include "SurfaceTracing.h" #include "TracedOrdinal.h" +#include "Tracing/LayerTracing.h" #include "TransactionCallbackInvoker.h" #include "TransactionState.h" @@ -135,6 +134,8 @@ enum { eTransactionMask = 0x1f, }; +enum class LatchUnsignaledConfig { Always, Auto, Disabled }; + using DisplayColorSetting = compositionengine::OutputColorSetting; struct SurfaceFlingerBE { @@ -246,18 +247,13 @@ public: static ui::Dataspace wideColorGamutCompositionDataspace; static ui::PixelFormat wideColorGamutCompositionPixelFormat; - // Whether to use frame rate API when deciding about the refresh rate of the display. This - // variable is caches in SF, so that we can check it with each layer creation, and a void the - // overhead that is caused by reading from sysprop. - static bool useFrameRateApi; - static constexpr SkipInitializationTag SkipInitialization; // Whether or not SDR layers should be dimmed to the desired SDR white point instead of // being treated as native display brightness static bool enableSdrDimming; - static bool enableLatchUnsignaled; + static LatchUnsignaledConfig enableLatchUnsignaledConfig; // must be called before clients can connect void init() ANDROID_API; @@ -268,10 +264,6 @@ public: SurfaceFlingerBE& getBE() { return mBE; } const SurfaceFlingerBE& getBE() const { return mBE; } - // Schedule an asynchronous or synchronous task on the main thread. - template <typename F, typename T = std::invoke_result_t<F>> - [[nodiscard]] std::future<T> schedule(F&&); - // Schedule commit of transactions on the main thread ahead of the next VSYNC. void scheduleCommit(FrameHint); // As above, but also force composite regardless if transactions were committed. @@ -326,6 +318,7 @@ public: // 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 @@ -355,7 +348,7 @@ private: friend class MonitoredProducer; friend class RefreshRateOverlay; friend class RegionSamplingThread; - friend class SurfaceTracing; + friend class LayerTracing; // For unit tests friend class TestableSurfaceFlinger; @@ -601,7 +594,6 @@ private: float lightPosY, float lightPosZ, float lightRadius) override; status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; - status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override; status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, const FrameTimelineInfo& frameTimelineInfo) override; @@ -649,7 +641,7 @@ private: // Toggles hardware VSYNC by calling into HWC. void setVsyncEnabled(bool) override; - // Initiates a refresh rate change to be applied on invalidate. + // Initiates a refresh rate change to be applied on commit. void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override; // Called when kernel idle timer has expired. Used to update the refresh rate overlay. void kernelTimerChanged(bool expired) override; @@ -687,9 +679,6 @@ private: const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) EXCLUDES(mStateLock); - // Returns whether transactions were committed. - bool flushAndCommitTransactions() EXCLUDES(mStateLock); - void commitTransactions() EXCLUDES(mStateLock); void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock); void doCommitTransactions() REQUIRES(mStateLock); @@ -751,7 +740,14 @@ private: const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, uid_t originUid, const Vector<ComposerState>& states, const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& - bufferLayersReadyToPresent) const REQUIRES(mStateLock); + bufferLayersReadyToPresent, + bool allowLatchUnsignaled) const REQUIRES(mStateLock); + static LatchUnsignaledConfig getLatchUnsignaledConfig(); + bool latchUnsignaledIsAllowed(std::vector<TransactionState>& transactions) REQUIRES(mStateLock); + bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock); + bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction) + REQUIRES(mStateLock); + bool applyTransactions(std::vector<TransactionState>& transactions) REQUIRES(mStateLock); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock); @@ -759,29 +755,23 @@ private: /* * Layer management */ - status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h, - PixelFormat format, uint32_t flags, LayerMetadata metadata, - sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, + status_t createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, const sp<IBinder>& parentHandle, int32_t* outLayerId, const sp<Layer>& parentLayer = nullptr, uint32_t* outTransformHint = nullptr); - status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w, - uint32_t h, uint32_t flags, LayerMetadata metadata, - PixelFormat& format, sp<IBinder>* outHandle, - sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer); + status_t createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format, + sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, + sp<Layer>* outLayer); - status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w, - uint32_t h, uint32_t flags, LayerMetadata metadata, - sp<IBinder>* outHandle, sp<Layer>* outLayer); + status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, + sp<Layer>* outLayer); - status_t createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h, - uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle, + status_t createEffectLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, sp<Layer>* outLayer); - status_t createContainerLayer(const sp<Client>& client, std::string name, uint32_t w, - uint32_t h, uint32_t flags, LayerMetadata metadata, - sp<IBinder>* outHandle, sp<Layer>* outLayer); + status_t createContainerLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, + sp<Layer>* outLayer); status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, int32_t* outLayerId); @@ -794,8 +784,7 @@ private: // add a layer to SurfaceFlinger status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc, - const wp<Layer>& parentLayer, bool addToRoot, + const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot, uint32_t* outTransformHint); // Traverse through all the layers and compute and cache its bounds. @@ -893,8 +882,6 @@ private: // 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; } @@ -952,10 +939,6 @@ private: 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); - struct FenceWithFenceTime { sp<Fence> fence = Fence::NO_FENCE; std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE; @@ -1049,12 +1032,12 @@ private: void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock); LayersProto dumpDrawingStateProto(uint32_t traceFlags) const; void dumpOffscreenLayersProto(LayersProto& layersProto, - uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; + uint32_t traceFlags = LayerTracing::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) + LayersProto dumpProtoFromMainThread(uint32_t traceFlags = LayerTracing::TRACE_ALL) EXCLUDES(mStateLock); void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock); void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); @@ -1067,8 +1050,6 @@ private: return doDump(fd, args, asProto); } - void onFrameRateFlexibilityTokenReleased(); - static mat4 calculateColorMatrix(float saturation); void updateColorMatrixLocked(); @@ -1117,16 +1098,12 @@ private: float mGlobalSaturationFactor = 1.0f; mat4 mClientColorMatrix; - // Can't be unordered_set because wp<> isn't hashable - std::set<wp<IBinder>> mGraphicBufferProducerList; size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS; // If there are more GraphicBufferProducers tracked by SurfaceFlinger than // this threshold, then begin logging. size_t mGraphicBufferProducerListSizeLogThreshold = static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS)); - void removeGraphicBufferProducerAsync(const wp<IBinder>&); - // protected by mStateLock (but we could use another lock) bool mLayersRemoved = false; bool mLayersAdded = false; @@ -1203,10 +1180,9 @@ private: bool mPropagateBackpressureClientComposition = false; sp<SurfaceInterceptor> mInterceptor; - SurfaceTracing mTracing{*this}; + LayerTracing mLayerTracing{*this}; std::mutex mTracingLock; bool mTracingEnabled = false; - bool mTracePostComposition = false; std::atomic<bool> mTracingEnabledChanged = false; const std::shared_ptr<TimeStats> mTimeStats; @@ -1223,14 +1199,9 @@ private: TransactionCallbackInvoker mTransactionCallbackInvoker; - // these are thread safe - std::unique_ptr<MessageQueue> mEventQueue; + // Thread-safe. FrameTracker mAnimFrameTracker; - // protected by mDestroyedLayerLock; - mutable Mutex mDestroyedLayerLock; - Vector<Layer const *> mDestroyedLayers; - // We maintain a pool of pre-generated texture names to hand out to avoid // layer creation needing to run on the main thread (which it would // otherwise need to do to access RenderEngine). @@ -1242,7 +1213,7 @@ private: Condition mTransactionQueueCV; std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mPendingTransactionQueues GUARDED_BY(mQueueLock); - std::queue<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock); + std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock); /* * Feature prototyping */ @@ -1335,29 +1306,18 @@ private: // be any issues with a raw pointer referencing an invalid object. std::unordered_set<Layer*> mOffscreenLayers; - int mFrameRateFlexibilityTokenCount = 0; - - sp<IBinder> mDebugFrameRateFlexibilityToken; - BufferCountTracker mBufferCountTracker; std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners GUARDED_BY(mStateLock); mutable Mutex mCreatedLayersLock; struct LayerCreatedState { - LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, - const wp<IBinder>& producer, bool addToRoot) - : layer(layer), - initialParent(parent), - initialProducer(producer), - addToRoot(addToRoot) {} + LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot) + : layer(layer), initialParent(parent), addToRoot(addToRoot) {} wp<Layer> layer; // Indicates the initial parent of the created layer, only used for creating layer in // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers. wp<Layer> initialParent; - // Indicates the initial graphic buffer producer of the created layer, only used for - // creating layer in SurfaceFlinger. - wp<IBinder> initialProducer; // Indicates whether the layer getting created should be added at root if there's no parent // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will // be added offscreen. @@ -1368,7 +1328,7 @@ private: // thread. std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers; void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer, - const wp<Layer> parent, const wp<IBinder>& producer, bool addToRoot); + const wp<Layer> parent, bool addToRoot); auto getLayerCreatedState(const sp<IBinder>& handle); sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock); diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp index 9a2f9107c3..b81b445dad 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp @@ -38,7 +38,6 @@ #include "SurfaceInterceptor.h" #include "DisplayHardware/ComposerHal.h" -#include "Scheduler/MessageQueue.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncConfiguration.h" #include "Scheduler/VsyncController.h" @@ -51,10 +50,6 @@ std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& return std::make_unique<android::impl::HWComposer>(serviceName); } -std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue(ICompositor& compositor) { - return std::make_unique<android::impl::MessageQueue>(compositor); -} - std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration( Fps currentRefreshRate) { if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) { @@ -64,12 +59,6 @@ std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfig } } -std::unique_ptr<Scheduler> DefaultFactory::createScheduler( - const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs, - ISchedulerCallback& callback) { - return std::make_unique<Scheduler>(std::move(refreshRateConfigs), callback); -} - sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() { return new android::impl::SurfaceInterceptor(); } diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h index 2be09ee788..501629d4da 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h @@ -27,11 +27,8 @@ public: virtual ~DefaultFactory(); std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override; - std::unique_ptr<MessageQueue> createMessageQueue(ICompositor&) override; std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) 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 bca533b794..e670f37189 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -77,11 +77,8 @@ class NativeWindowSurface; class Factory { public: virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0; - virtual std::unique_ptr<MessageQueue> createMessageQueue(ICompositor&) = 0; virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) = 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/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index a8117f7f57..16f6e31946 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -304,14 +304,6 @@ bool support_kernel_idle_timer(bool defaultValue) { return defaultValue; } -bool use_frame_rate_api(bool defaultValue) { - auto temp = SurfaceFlingerProperties::use_frame_rate_api(); - if (temp.has_value()) { - return *temp; - } - return defaultValue; -} - bool enable_sdr_dimming(bool defaultValue) { return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue); } diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index ed182608fb..8d0e4263b1 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -88,8 +88,6 @@ bool enable_protected_contents(bool defaultValue); bool support_kernel_idle_timer(bool defaultValue); -bool use_frame_rate_api(bool defaultValue); - int32_t display_update_imminent_timeout_ms(int32_t defaultValue); android::ui::DisplayPrimaries getDisplayNativePrimaries(); diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp deleted file mode 100644 index 596373732f..0000000000 --- a/services/surfaceflinger/SurfaceTracing.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2017 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 "SurfaceTracing" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "SurfaceTracing.h" -#include <SurfaceFlinger.h> - -#include <android-base/file.h> -#include <android-base/stringprintf.h> -#include <log/log.h> -#include <utils/SystemClock.h> -#include <utils/Trace.h> - -namespace android { - -SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {} - -bool SurfaceTracing::enable() { - std::scoped_lock lock(mTraceLock); - if (mEnabled) { - return false; - } - - if (flagIsSet(TRACE_SYNC)) { - runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig); - } else { - runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig, - mFlinger.mTracingLock); - } - mEnabled = true; - return true; -} - -bool SurfaceTracing::disable() { - std::scoped_lock lock(mTraceLock); - if (!mEnabled) { - return false; - } - mEnabled = false; - runner->stop(); - return true; -} - -bool SurfaceTracing::isEnabled() const { - std::scoped_lock lock(mTraceLock); - return mEnabled; -} - -status_t SurfaceTracing::writeToFile() { - std::scoped_lock lock(mTraceLock); - if (!mEnabled) { - return STATUS_OK; - } - return runner->writeToFile(); -} - -void SurfaceTracing::notify(const char* where) { - std::scoped_lock lock(mTraceLock); - if (mEnabled) { - runner->notify(where); - } -} - -void SurfaceTracing::notifyLocked(const char* where) { - std::scoped_lock lock(mTraceLock); - if (mEnabled) { - runner->notifyLocked(where); - } -} - -void SurfaceTracing::dump(std::string& result) const { - std::scoped_lock lock(mTraceLock); - base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled"); - if (mEnabled) { - runner->dump(result); - } -} - -void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) { - // use the swap trick to make sure memory is released - std::queue<LayersTraceProto>().swap(mStorage); - mSizeInBytes = newSize; - mUsedInBytes = 0U; -} - -void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) { - size_t protoSize = static_cast<size_t>(proto.ByteSize()); - while (mUsedInBytes + protoSize > mSizeInBytes) { - if (mStorage.empty()) { - return; - } - mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize()); - mStorage.pop(); - } - mUsedInBytes += protoSize; - mStorage.emplace(); - mStorage.back().Swap(&proto); -} - -void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) { - fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size())); - - while (!mStorage.empty()) { - auto entry = fileProto->add_entry(); - entry->Swap(&mStorage.front()); - mStorage.pop(); - } -} - -SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config) - : mFlinger(flinger), mConfig(config) { - mBuffer.setSize(mConfig.bufferSize); -} - -void SurfaceTracing::Runner::notify(const char* where) { - LayersTraceProto entry = traceLayers(where); - mBuffer.emplace(std::move(entry)); -} - -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 = createLayersTraceFileProto(); - std::string output; - - mBuffer.flush(&fileProto); - mBuffer.reset(mConfig.bufferSize); - - if (!fileProto.SerializeToString(&output)) { - ALOGE("Could not save the proto file! Permission denied"); - return PERMISSION_DENIED; - } - - // -rw-r--r-- - const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(), - true)) { - ALOGE("Could not save the proto file! There are missing fields"); - return PERMISSION_DENIED; - } - - return NO_ERROR; -} - -LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) { - ATRACE_CALL(); - - LayersTraceProto entry; - entry.set_elapsed_realtime_nanos(elapsedRealtimeNano()); - entry.set_where(where); - LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags)); - - if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) { - mFlinger.dumpOffscreenLayersProto(layers); - } - entry.mutable_layers()->Swap(&layers); - - if (flagIsSet(SurfaceTracing::TRACE_HWC)) { - std::string hwcDump; - mFlinger.dumpHwc(hwcDump); - entry.set_hwc_blob(hwcDump); - } - if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) { - entry.set_excludes_composition_state(true); - } - entry.set_missed_entries(mMissedTraceEntries); - mFlinger.dumpDisplayProto(entry); - return entry; -} - -void SurfaceTracing::Runner::dump(std::string& result) const { - base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n", - mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB), - float(mBuffer.size()) / float(1_MB)); -} - -SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, - std::mutex& sfLock) - : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) { - mEnabled = true; - mThread = std::thread(&AsyncRunner::loop, this); -} - -void SurfaceTracing::AsyncRunner::loop() { - while (mEnabled) { - LayersTraceProto entry; - bool entryAdded = traceWhenNotified(&entry); - if (entryAdded) { - mBuffer.emplace(std::move(entry)); - } - if (mWriteToFile) { - Runner::writeToFile(); - mWriteToFile = false; - } - } -} - -bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) { - std::unique_lock<std::mutex> lock(mSfLock); - mCanStartTrace.wait(lock); - if (!mAddEntry) { - return false; - } - *outProto = traceLayers(mWhere); - mAddEntry = false; - mMissedTraceEntries = 0; - return true; -} - -void SurfaceTracing::AsyncRunner::notify(const char* where) { - std::scoped_lock lock(mSfLock); - notifyLocked(where); -} - -void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) { - mWhere = where; - if (mAddEntry) { - mMissedTraceEntries++; - } - mAddEntry = true; - mCanStartTrace.notify_one(); -} - -status_t SurfaceTracing::AsyncRunner::writeToFile() { - mWriteToFile = true; - mCanStartTrace.notify_one(); - return STATUS_OK; -} - -status_t SurfaceTracing::AsyncRunner::stop() { - mEnabled = false; - mCanStartTrace.notify_one(); - mThread.join(); - return Runner::writeToFile(); -} - -} // namespace android diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h deleted file mode 100644 index cea1a3324e..0000000000 --- a/services/surfaceflinger/SurfaceTracing.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <android-base/thread_annotations.h> -#include <layerproto/LayerProtoHeader.h> -#include <utils/Errors.h> -#include <utils/StrongPointer.h> - -#include <condition_variable> -#include <memory> -#include <mutex> -#include <queue> -#include <thread> - -using namespace android::surfaceflinger; - -namespace android { - -class SurfaceFlinger; -constexpr auto operator""_MB(unsigned long long const num) { - return num * 1024 * 1024; -} -/* - * SurfaceTracing records layer states during surface flinging. Manages tracing state and - * configuration. - */ -class SurfaceTracing { -public: - SurfaceTracing(SurfaceFlinger& flinger); - bool enable(); - bool disable(); - status_t writeToFile(); - bool isEnabled() const; - /* - * Adds a trace entry, must be called from the drawing thread or while holding the - * SurfaceFlinger tracing lock. - */ - void notify(const char* where); - /* - * Adds a trace entry, called while holding the SurfaceFlinger tracing lock. - */ - void notifyLocked(const char* where) /* REQUIRES(mSfLock) */; - - void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; } - void dump(std::string& result) const; - - enum : uint32_t { - TRACE_CRITICAL = 1 << 0, - TRACE_INPUT = 1 << 1, - TRACE_COMPOSITION = 1 << 2, - TRACE_EXTRA = 1 << 3, - TRACE_HWC = 1 << 4, - // Add non-geometry composition changes to the trace. - TRACE_BUFFERS = 1 << 5, - // Add entries from the drawing thread post composition. - TRACE_SYNC = 1 << 6, - TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA, - }; - 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.winscope"; - - SurfaceFlinger& mFlinger; - mutable std::mutex mTraceLock; - bool mEnabled GUARDED_BY(mTraceLock) = false; - std::unique_ptr<Runner> runner GUARDED_BY(mTraceLock); - - struct Config { - uint32_t flags = TRACE_CRITICAL | TRACE_INPUT | TRACE_SYNC; - size_t bufferSize = DEFAULT_BUFFER_SIZE; - } mConfig; - - /* - * ring buffer. - */ - class LayersTraceBuffer { - public: - size_t size() const { return mSizeInBytes; } - size_t used() const { return mUsedInBytes; } - size_t frameCount() const { return mStorage.size(); } - - void setSize(size_t newSize) { mSizeInBytes = newSize; } - void reset(size_t newSize); - void emplace(LayersTraceProto&& proto); - void flush(LayersTraceFileProto* fileProto); - - private: - size_t mUsedInBytes = 0U; - size_t mSizeInBytes = DEFAULT_BUFFER_SIZE; - std::queue<LayersTraceProto> mStorage; - }; - - /* - * Implements a synchronous way of adding trace entries. This must be called - * from the drawing thread. - */ - class Runner { - public: - Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config); - virtual ~Runner() = default; - virtual status_t stop(); - virtual status_t writeToFile(); - virtual void notify(const char* where); - /* Cannot be called with a synchronous runner. */ - virtual void notifyLocked(const char* /* where */) {} - void dump(std::string& result) const; - - protected: - bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; } - SurfaceFlinger& mFlinger; - SurfaceTracing::Config mConfig; - SurfaceTracing::LayersTraceBuffer mBuffer; - uint32_t mMissedTraceEntries = 0; - LayersTraceProto traceLayers(const char* where); - }; - - /* - * Implements asynchronous way to add trace entries called from a separate thread while holding - * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not - * scheduled in time. - */ - class AsyncRunner : public Runner { - public: - AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock); - virtual ~AsyncRunner() = default; - status_t stop() override; - status_t writeToFile() override; - void notify(const char* where) override; - void notifyLocked(const char* where); - - private: - std::mutex& mSfLock; - std::condition_variable mCanStartTrace; - std::thread mThread; - const char* mWhere = ""; - bool mWriteToFile = false; - bool mEnabled = false; - bool mAddEntry = false; - void loop(); - bool traceWhenNotified(LayersTraceProto* outProto); - }; -}; - -} // namespace android diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp new file mode 100644 index 0000000000..84890eefd1 --- /dev/null +++ b/services/surfaceflinger/Tracing/LayerTracing.cpp @@ -0,0 +1,130 @@ +/* + * 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 "LayerTracing" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <SurfaceFlinger.h> +#include <android-base/stringprintf.h> +#include <log/log.h> +#include <utils/SystemClock.h> +#include <utils/Trace.h> + +#include "LayerTracing.h" +#include "RingBuffer.h" + +namespace android { + +LayerTracing::LayerTracing(SurfaceFlinger& flinger) : mFlinger(flinger) { + mBuffer = std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>(); +} + +LayerTracing::~LayerTracing() = default; + +bool LayerTracing::enable() { + std::scoped_lock lock(mTraceLock); + if (mEnabled) { + return false; + } + mBuffer->setSize(mBufferSizeInBytes); + mEnabled = true; + return true; +} + +bool LayerTracing::disable() { + std::scoped_lock lock(mTraceLock); + if (!mEnabled) { + return false; + } + mEnabled = false; + LayersTraceFileProto fileProto = createTraceFileProto(); + mBuffer->writeToFile(fileProto, FILE_NAME); + return true; +} + +bool LayerTracing::isEnabled() const { + std::scoped_lock lock(mTraceLock); + return mEnabled; +} + +status_t LayerTracing::writeToFile() { + std::scoped_lock lock(mTraceLock); + if (!mEnabled) { + return STATUS_OK; + } + LayersTraceFileProto fileProto = createTraceFileProto(); + return mBuffer->writeToFile(fileProto, FILE_NAME); +} + +void LayerTracing::setTraceFlags(uint32_t flags) { + std::scoped_lock lock(mTraceLock); + mFlags = flags; +} + +void LayerTracing::setBufferSize(size_t bufferSizeInBytes) { + std::scoped_lock lock(mTraceLock); + mBufferSizeInBytes = bufferSizeInBytes; +} + +bool LayerTracing::flagIsSet(uint32_t flags) const { + return (mFlags & flags) == flags; +} + +LayersTraceFileProto LayerTracing::createTraceFileProto() const { + LayersTraceFileProto fileProto; + fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | + LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); + return fileProto; +} + +void LayerTracing::dump(std::string& result) const { + std::scoped_lock lock(mTraceLock); + base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled"); + mBuffer->dump(result); +} + +void LayerTracing::notify(const char* where) { + ATRACE_CALL(); + std::scoped_lock lock(mTraceLock); + if (!mEnabled) { + return; + } + + ATRACE_CALL(); + LayersTraceProto entry; + entry.set_elapsed_realtime_nanos(elapsedRealtimeNano()); + entry.set_where(where); + LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags)); + + if (flagIsSet(LayerTracing::TRACE_EXTRA)) { + mFlinger.dumpOffscreenLayersProto(layers); + } + entry.mutable_layers()->Swap(&layers); + + if (flagIsSet(LayerTracing::TRACE_HWC)) { + std::string hwcDump; + mFlinger.dumpHwc(hwcDump); + entry.set_hwc_blob(hwcDump); + } + if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) { + entry.set_excludes_composition_state(true); + } + mFlinger.dumpDisplayProto(entry); + mBuffer->emplace(std::move(entry)); +} + +} // namespace android diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h new file mode 100644 index 0000000000..8ca3587dbc --- /dev/null +++ b/services/surfaceflinger/Tracing/LayerTracing.h @@ -0,0 +1,77 @@ +/* + * 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-base/thread_annotations.h> +#include <layerproto/LayerProtoHeader.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> +#include <utils/Timers.h> + +#include <memory> +#include <mutex> + +using namespace android::surfaceflinger; + +namespace android { + +template <typename FileProto, typename EntryProto> +class RingBuffer; + +class SurfaceFlinger; + +/* + * LayerTracing records layer states during surface flinging. Manages tracing state and + * configuration. + */ +class LayerTracing { +public: + LayerTracing(SurfaceFlinger& flinger); + ~LayerTracing(); + bool enable(); + bool disable(); + bool isEnabled() const; + status_t writeToFile(); + LayersTraceFileProto createTraceFileProto() const; + void notify(const char* where); + + enum : uint32_t { + TRACE_INPUT = 1 << 1, + TRACE_COMPOSITION = 1 << 2, + TRACE_EXTRA = 1 << 3, + TRACE_HWC = 1 << 4, + TRACE_BUFFERS = 1 << 5, + TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA, + }; + void setTraceFlags(uint32_t flags); + bool flagIsSet(uint32_t flags) const; + void setBufferSize(size_t bufferSizeInBytes); + void dump(std::string&) const; + +private: + static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope"; + + SurfaceFlinger& mFlinger; + uint32_t mFlags = TRACE_INPUT; + mutable std::mutex mTraceLock; + bool mEnabled GUARDED_BY(mTraceLock) = false; + std::unique_ptr<RingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer + GUARDED_BY(mTraceLock); + size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = 20 * 1024 * 1024; +}; + +} // namespace android diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h new file mode 100644 index 0000000000..d0fb3f28fe --- /dev/null +++ b/services/surfaceflinger/Tracing/RingBuffer.h @@ -0,0 +1,112 @@ +/* + * 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-base/file.h> +#include <android-base/stringprintf.h> + +#include <log/log.h> +#include <utils/Errors.h> +#include <utils/SystemClock.h> +#include <utils/Trace.h> +#include <queue> + +namespace android { + +class SurfaceFlinger; + +template <typename FileProto, typename EntryProto> +class RingBuffer { +public: + size_t size() const { return mSizeInBytes; } + size_t used() const { return mUsedInBytes; } + size_t frameCount() const { return mStorage.size(); } + void setSize(size_t newSize) { mSizeInBytes = newSize; } + EntryProto& front() { return mStorage.front(); } + const EntryProto& front() const { return mStorage.front(); } + + void reset(size_t newSize) { + // use the swap trick to make sure memory is released + std::queue<EntryProto>().swap(mStorage); + mSizeInBytes = newSize; + mUsedInBytes = 0U; + } + void flush(FileProto& fileProto) { + fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size())); + while (!mStorage.empty()) { + auto entry = fileProto.add_entry(); + entry->Swap(&mStorage.front()); + mStorage.pop(); + } + } + + status_t writeToFile(FileProto& fileProto, std::string filename) { + ATRACE_CALL(); + std::string output; + flush(fileProto); + reset(mSizeInBytes); + if (!fileProto.SerializeToString(&output)) { + ALOGE("Could not serialize proto."); + return UNKNOWN_ERROR; + } + + // -rw-r--r-- + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (!android::base::WriteStringToFile(output, filename, mode, getuid(), getgid(), true)) { + ALOGE("Could not save the proto file."); + return PERMISSION_DENIED; + } + return NO_ERROR; + } + + std::vector<EntryProto> emplace(EntryProto&& proto) { + std::vector<EntryProto> replacedEntries; + size_t protoSize = static_cast<size_t>(proto.ByteSize()); + while (mUsedInBytes + protoSize > mSizeInBytes) { + if (mStorage.empty()) { + return {}; + } + mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize()); + replacedEntries.emplace_back(mStorage.front()); + mStorage.pop(); + } + mUsedInBytes += protoSize; + mStorage.emplace(); + mStorage.back().Swap(&proto); + return replacedEntries; + } + + void dump(std::string& result) const { + std::chrono::milliseconds duration(0); + if (frameCount() > 0) { + duration = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::nanoseconds(systemTime() - front().elapsed_realtime_nanos())); + } + const int64_t durationCount = duration.count(); + base::StringAppendF(&result, + " number of entries: %zu (%.2fMB / %.2fMB) duration: %" PRIi64 "ms\n", + frameCount(), float(used()) / 1024.f * 1024.f, + float(size()) / 1024.f * 1024.f, durationCount); + } + +private: + size_t mUsedInBytes = 0U; + size_t mSizeInBytes = 0U; + std::queue<EntryProto> mStorage; +}; + +} // namespace android diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index fb1d43bf6c..d1dc07639a 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -22,9 +22,9 @@ namespace android::surfaceflinger { -proto::TransactionState TransactionProtoParser::toProto( - const TransactionState& t, std::function<int32_t(const sp<IBinder>&)> getLayerId, - std::function<int32_t(const sp<IBinder>&)> getDisplayId) { +proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t, + LayerHandleToIdFn getLayerId, + DisplayHandleToIdFn getDisplayId) { proto::TransactionState proto; proto.set_pid(t.originPid); proto.set_uid(t.originUid); @@ -42,10 +42,38 @@ proto::TransactionState TransactionProtoParser::toProto( return proto; } -proto::LayerState TransactionProtoParser::toProto( - const layer_state_t& layer, std::function<int32_t(const sp<IBinder>&)> getLayerId) { +proto::TransactionState TransactionProtoParser::toProto( + std::vector<std::pair<int32_t /* layerId */, TracingLayerState>> states) { + proto::TransactionState proto; + for (auto& [layerId, state] : states) { + proto::LayerState layerProto = toProto(state, nullptr); + if (layerProto.has_buffer_data()) { + proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data(); + bufferProto->set_buffer_id(state.bufferId); + bufferProto->set_width(state.bufferWidth); + bufferProto->set_height(state.bufferHeight); + } + layerProto.set_has_sideband_stream(state.hasSidebandStream); + layerProto.set_layer_id(state.layerId); + layerProto.set_parent_id(state.parentId); + layerProto.set_relative_parent_id(state.relativeParentId); + if (layerProto.has_window_info_handle()) { + layerProto.mutable_window_info_handle()->set_crop_layer_id(state.inputCropId); + } + proto.mutable_layer_changes()->Add(std::move(layerProto)); + } + return proto; +} + +proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer, + LayerHandleToIdFn getLayerId) { proto::LayerState proto; - proto.set_layer_id(layer.layerId); + if (getLayerId != nullptr) { + proto.set_layer_id(getLayerId(layer.surface)); + } else { + proto.set_layer_id(layer.layerId); + } + proto.set_what(layer.what); if (layer.what & layer_state_t::ePositionChanged) { @@ -130,13 +158,13 @@ proto::LayerState TransactionProtoParser::toProto( } } - if (layer.what & layer_state_t::eReparent) { + if ((layer.what & layer_state_t::eReparent) && getLayerId != nullptr) { int32_t layerId = layer.parentSurfaceControlForChild ? getLayerId(layer.parentSurfaceControlForChild->getHandle()) : -1; proto.set_parent_id(layerId); } - if (layer.what & layer_state_t::eRelativeLayerChanged) { + if ((layer.what & layer_state_t::eRelativeLayerChanged) && getLayerId != nullptr) { int32_t layerId = layer.relativeLayerSurfaceControl ? getLayerId(layer.relativeLayerSurfaceControl->getHandle()) : -1; @@ -164,8 +192,12 @@ proto::LayerState TransactionProtoParser::toProto( transformProto->set_ty(inputInfo->transform.ty()); windowInfoProto->set_replace_touchable_region_with_crop( inputInfo->replaceTouchableRegionWithCrop); - windowInfoProto->set_crop_layer_id( - getLayerId(inputInfo->touchableRegionCropHandle.promote())); + if (getLayerId != nullptr) { + windowInfoProto->set_crop_layer_id( + getLayerId(inputInfo->touchableRegionCropHandle.promote())); + } else { + windowInfoProto->set_crop_layer_id(-1); + } } } if (layer.what & layer_state_t::eBackgroundColorChanged) { @@ -212,11 +244,13 @@ proto::LayerState TransactionProtoParser::toProto( return proto; } -proto::DisplayState TransactionProtoParser::toProto( - const DisplayState& display, std::function<int32_t(const sp<IBinder>&)> getDisplayId) { +proto::DisplayState TransactionProtoParser::toProto(const DisplayState& display, + DisplayHandleToIdFn getDisplayId) { proto::DisplayState proto; proto.set_what(display.what); - proto.set_id(getDisplayId(display.token)); + if (getDisplayId != nullptr) { + proto.set_id(getDisplayId(display.token)); + } if (display.what & DisplayState::eLayerStackChanged) { proto.set_layer_stack(display.layerStack.id); @@ -238,9 +272,18 @@ proto::DisplayState TransactionProtoParser::toProto( return proto; } -TransactionState TransactionProtoParser::fromProto( - const proto::TransactionState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle, - std::function<sp<IBinder>(int32_t)> getDisplayHandle) { +proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) { + proto::LayerCreationArgs proto; + proto.set_layer_id(args.layerId); + proto.set_name(args.name); + proto.set_flags(args.flags); + proto.set_parent_id(args.parentId); + return proto; +} + +TransactionState TransactionProtoParser::fromProto(const proto::TransactionState& proto, + LayerIdToHandleFn getLayerHandle, + DisplayIdToHandleFn getDisplayHandle) { TransactionState t; t.originPid = proto.pid(); t.originUid = proto.uid(); @@ -251,7 +294,7 @@ TransactionState TransactionProtoParser::fromProto( t.states.reserve(static_cast<size_t>(layerCount)); for (int i = 0; i < layerCount; i++) { ComposerState s; - s.state = std::move(fromProto(proto.layer_changes(i), getLayerHandle)); + fromProto(proto.layer_changes(i), getLayerHandle, s.state); t.states.add(s); } @@ -263,88 +306,116 @@ TransactionState TransactionProtoParser::fromProto( return t; } -layer_state_t TransactionProtoParser::fromProto( - const proto::LayerState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle) { - layer_state_t layer; +void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto, + TracingLayerCreationArgs& outArgs) { + outArgs.layerId = proto.layer_id(); + outArgs.name = proto.name(); + outArgs.flags = proto.flags(); + outArgs.parentId = proto.parent_id(); +} + +void TransactionProtoParser::fromProto(const proto::LayerState& proto, + LayerIdToHandleFn getLayerHandle, + TracingLayerState& outState) { + fromProto(proto, getLayerHandle, static_cast<layer_state_t&>(outState)); + if (proto.what() & layer_state_t::eReparent) { + outState.parentId = proto.parent_id(); + } + if (proto.what() & layer_state_t::eRelativeLayerChanged) { + outState.relativeParentId = proto.relative_parent_id(); + } + if (proto.what() & layer_state_t::eInputInfoChanged) { + outState.inputCropId = proto.window_info_handle().crop_layer_id(); + } + if (proto.what() & layer_state_t::eBufferChanged) { + const proto::LayerState_BufferData& bufferProto = proto.buffer_data(); + outState.bufferId = bufferProto.buffer_id(); + outState.bufferWidth = bufferProto.width(); + outState.bufferHeight = bufferProto.height(); + } + if (proto.what() & layer_state_t::eSidebandStreamChanged) { + outState.hasSidebandStream = proto.has_sideband_stream(); + } +} + +void TransactionProtoParser::fromProto(const proto::LayerState& proto, + LayerIdToHandleFn getLayerHandle, layer_state_t& layer) { layer.layerId = proto.layer_id(); - layer.what = proto.what(); + layer.what |= proto.what(); - if (layer.what & layer_state_t::ePositionChanged) { + if (getLayerHandle != nullptr) { + layer.surface = getLayerHandle(layer.layerId); + } + + if (proto.what() & layer_state_t::ePositionChanged) { layer.x = proto.x(); layer.y = proto.y(); } - if (layer.what & layer_state_t::eLayerChanged) { + if (proto.what() & layer_state_t::eLayerChanged) { layer.z = proto.z(); } - if (layer.what & layer_state_t::eSizeChanged) { + if (proto.what() & layer_state_t::eSizeChanged) { layer.w = proto.w(); layer.h = proto.h(); } - if (layer.what & layer_state_t::eLayerStackChanged) { + if (proto.what() & layer_state_t::eLayerStackChanged) { layer.layerStack.id = proto.layer_stack(); } - if (layer.what & layer_state_t::eFlagsChanged) { + if (proto.what() & layer_state_t::eFlagsChanged) { layer.flags = proto.flags(); layer.mask = proto.mask(); } - if (layer.what & layer_state_t::eMatrixChanged) { + if (proto.what() & layer_state_t::eMatrixChanged) { const proto::LayerState_Matrix22& matrixProto = proto.matrix(); layer.matrix.dsdx = matrixProto.dsdx(); layer.matrix.dsdy = matrixProto.dsdy(); layer.matrix.dtdx = matrixProto.dtdx(); layer.matrix.dtdy = matrixProto.dtdy(); } - if (layer.what & layer_state_t::eCornerRadiusChanged) { + if (proto.what() & layer_state_t::eCornerRadiusChanged) { layer.cornerRadius = proto.corner_radius(); } - if (layer.what & layer_state_t::eBackgroundBlurRadiusChanged) { + if (proto.what() & layer_state_t::eBackgroundBlurRadiusChanged) { layer.backgroundBlurRadius = proto.background_blur_radius(); } - if (layer.what & layer_state_t::eAlphaChanged) { + if (proto.what() & layer_state_t::eAlphaChanged) { layer.alpha = proto.alpha(); } - if (layer.what & layer_state_t::eColorChanged) { + if (proto.what() & layer_state_t::eColorChanged) { const proto::LayerState_Color3& colorProto = proto.color(); layer.color.r = colorProto.r(); layer.color.g = colorProto.g(); layer.color.b = colorProto.b(); } - if (layer.what & layer_state_t::eTransparentRegionChanged) { + if (proto.what() & layer_state_t::eTransparentRegionChanged) { LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion); } - if (layer.what & layer_state_t::eTransformChanged) { + if (proto.what() & layer_state_t::eTransformChanged) { layer.transform = proto.transform(); } - if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) { + if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) { layer.transformToDisplayInverse = proto.transform_to_display_inverse(); } - if (layer.what & layer_state_t::eCropChanged) { + if (proto.what() & layer_state_t::eCropChanged) { LayerProtoHelper::readFromProto(proto.crop(), layer.crop); } - if (layer.what & layer_state_t::eBufferChanged) { + if (proto.what() & layer_state_t::eBufferChanged) { const proto::LayerState_BufferData& bufferProto = proto.buffer_data(); - layer.bufferData.buffer = new GraphicBuffer(bufferProto.width(), bufferProto.height(), - HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); layer.bufferData.frameNumber = bufferProto.frame_number(); layer.bufferData.flags = Flags<BufferData::BufferDataChange>(bufferProto.flags()); layer.bufferData.cachedBuffer.id = bufferProto.cached_buffer_id(); } - if (layer.what & layer_state_t::eSidebandStreamChanged) { - native_handle_t* handle = native_handle_create(0, 0); - layer.sidebandStream = - proto.has_sideband_stream() ? NativeHandle::create(handle, true) : nullptr; - } - if (layer.what & layer_state_t::eApiChanged) { + if (proto.what() & layer_state_t::eApiChanged) { layer.api = proto.api(); } - if (layer.what & layer_state_t::eColorTransformChanged) { + if (proto.what() & layer_state_t::eColorTransformChanged) { LayerProtoHelper::readFromProto(proto.color_transform(), layer.colorTransform); } - if (layer.what & layer_state_t::eBlurRegionsChanged) { + if (proto.what() & layer_state_t::eBlurRegionsChanged) { layer.blurRegions.reserve(static_cast<size_t>(proto.blur_regions_size())); for (int i = 0; i < proto.blur_regions_size(); i++) { android::BlurRegion region; @@ -353,20 +424,20 @@ layer_state_t TransactionProtoParser::fromProto( } } - if (layer.what & layer_state_t::eReparent) { + if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) { int32_t layerId = proto.parent_id(); layer.parentSurfaceControlForChild = new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId), nullptr, layerId); } - if (layer.what & layer_state_t::eRelativeLayerChanged) { + if ((proto.what() & layer_state_t::eRelativeLayerChanged) && (getLayerHandle != nullptr)) { int32_t layerId = proto.relative_parent_id(); layer.relativeLayerSurfaceControl = new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId), nullptr, layerId); } - if ((layer.what & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) { + if ((proto.what() & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) { gui::WindowInfo inputInfo; const proto::LayerState_WindowInfo& windowInfoProto = proto.window_info_handle(); @@ -385,10 +456,12 @@ layer_state_t TransactionProtoParser::fromProto( inputInfo.replaceTouchableRegionWithCrop = windowInfoProto.replace_touchable_region_with_crop(); int32_t layerId = windowInfoProto.crop_layer_id(); - inputInfo.touchableRegionCropHandle = getLayerHandle(layerId); + if (getLayerHandle != nullptr) { + inputInfo.touchableRegionCropHandle = getLayerHandle(layerId); + } layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo); } - if (layer.what & layer_state_t::eBackgroundColorChanged) { + if (proto.what() & layer_state_t::eBackgroundColorChanged) { layer.bgColorAlpha = proto.bg_color_alpha(); layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace()); const proto::LayerState_Color3& colorProto = proto.color(); @@ -396,44 +469,43 @@ layer_state_t TransactionProtoParser::fromProto( layer.color.g = colorProto.g(); layer.color.b = colorProto.b(); } - if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) { + if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) { layer.colorSpaceAgnostic = proto.color_space_agnostic(); } - if (layer.what & layer_state_t::eShadowRadiusChanged) { + if (proto.what() & layer_state_t::eShadowRadiusChanged) { layer.shadowRadius = proto.shadow_radius(); } - if (layer.what & layer_state_t::eFrameRateSelectionPriority) { + if (proto.what() & layer_state_t::eFrameRateSelectionPriority) { layer.frameRateSelectionPriority = proto.frame_rate_selection_priority(); } - if (layer.what & layer_state_t::eFrameRateChanged) { + if (proto.what() & layer_state_t::eFrameRateChanged) { layer.frameRate = proto.frame_rate(); layer.frameRateCompatibility = static_cast<int8_t>(proto.frame_rate_compatibility()); layer.changeFrameRateStrategy = static_cast<int8_t>(proto.change_frame_rate_strategy()); } - if (layer.what & layer_state_t::eFixedTransformHintChanged) { + if (proto.what() & layer_state_t::eFixedTransformHintChanged) { layer.fixedTransformHint = static_cast<ui::Transform::RotationFlags>(proto.fixed_transform_hint()); } - if (layer.what & layer_state_t::eAutoRefreshChanged) { + if (proto.what() & layer_state_t::eAutoRefreshChanged) { layer.autoRefresh = proto.auto_refresh(); } - if (layer.what & layer_state_t::eTrustedOverlayChanged) { + if (proto.what() & layer_state_t::eTrustedOverlayChanged) { layer.isTrustedOverlay = proto.is_trusted_overlay(); } - if (layer.what & layer_state_t::eBufferCropChanged) { + if (proto.what() & layer_state_t::eBufferCropChanged) { LayerProtoHelper::readFromProto(proto.buffer_crop(), layer.bufferCrop); } - if (layer.what & layer_state_t::eDestinationFrameChanged) { + if (proto.what() & layer_state_t::eDestinationFrameChanged) { LayerProtoHelper::readFromProto(proto.destination_frame(), layer.destinationFrame); } - if (layer.what & layer_state_t::eDropInputModeChanged) { + if (proto.what() & layer_state_t::eDropInputModeChanged) { layer.dropInputMode = static_cast<gui::DropInputMode>(proto.drop_input_mode()); } - return layer; } -DisplayState TransactionProtoParser::fromProto( - const proto::DisplayState& proto, std::function<sp<IBinder>(int32_t)> getDisplayHandle) { +DisplayState TransactionProtoParser::fromProto(const proto::DisplayState& proto, + DisplayIdToHandleFn getDisplayHandle) { DisplayState display; display.what = proto.what(); display.token = getDisplayHandle(proto.id()); diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h index a2b8889eb7..e8a139fb98 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.h +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h @@ -21,24 +21,53 @@ #include "TransactionState.h" namespace android::surfaceflinger { + +struct TracingLayerCreationArgs { + int32_t layerId; + std::string name; + uint32_t flags; + int32_t parentId; +}; + +struct TracingLayerState : layer_state_t { + uint64_t bufferId; + uint32_t bufferHeight; + uint32_t bufferWidth; + bool hasSidebandStream; + int32_t parentId; + int32_t relativeParentId; + int32_t inputCropId; + std::string name; + uint32_t layerCreationFlags; +}; + class TransactionProtoParser { public: + typedef std::function<sp<IBinder>(int32_t)> LayerIdToHandleFn; + typedef std::function<sp<IBinder>(int32_t)> DisplayIdToHandleFn; + typedef std::function<int32_t(const sp<IBinder>&)> LayerHandleToIdFn; + typedef std::function<int32_t(const sp<IBinder>&)> DisplayHandleToIdFn; + + static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn, + DisplayHandleToIdFn getDisplayIdFn); static proto::TransactionState toProto( - const TransactionState&, std::function<int32_t(const sp<IBinder>&)> getLayerIdFn, - std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn); + std::vector<std::pair<int32_t /* layerId */, TracingLayerState>>); + + static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args); + static TransactionState fromProto(const proto::TransactionState&, - std::function<sp<IBinder>(int32_t)> getLayerHandleFn, - std::function<sp<IBinder>(int32_t)> getDisplayHandleFn); + LayerIdToHandleFn getLayerHandleFn, + DisplayIdToHandleFn getDisplayHandleFn); + static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn, + TracingLayerState& outState); + static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs); private: - static proto::LayerState toProto(const layer_state_t&, - std::function<int32_t(const sp<IBinder>&)> getLayerId); - static proto::DisplayState toProto(const DisplayState&, - std::function<int32_t(const sp<IBinder>&)> getDisplayId); - static layer_state_t fromProto(const proto::LayerState&, - std::function<sp<IBinder>(int32_t)> getLayerHandle); - static DisplayState fromProto(const proto::DisplayState&, - std::function<sp<IBinder>(int32_t)> getDisplayHandle); + static proto::LayerState toProto(const layer_state_t&, LayerHandleToIdFn getLayerId); + static proto::DisplayState toProto(const DisplayState&, DisplayHandleToIdFn getDisplayId); + static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandle, + layer_state_t& out); + static DisplayState fromProto(const proto::DisplayState&, DisplayIdToHandleFn getDisplayHandle); }; } // namespace android::surfaceflinger
\ No newline at end of file diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h index 935502a6e1..802d22d86a 100644 --- a/services/surfaceflinger/TunnelModeEnabledReporter.h +++ b/services/surfaceflinger/TunnelModeEnabledReporter.h @@ -22,6 +22,8 @@ #include <unordered_map> +#include "WpHash.h" + namespace android { class Layer; @@ -54,11 +56,6 @@ public: private: mutable std::mutex mMutex; - struct WpHash { - size_t operator()(const wp<IBinder>& p) const { - return std::hash<IBinder*>()(p.unsafe_get()); - } - }; std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners GUARDED_BY(mMutex); diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h index ecd797a631..4e08393940 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.h +++ b/services/surfaceflinger/WindowInfosListenerInvoker.h @@ -23,6 +23,8 @@ #include <utils/Mutex.h> #include <unordered_map> +#include "WpHash.h" + namespace android { class SurfaceFlinger; @@ -42,12 +44,6 @@ protected: 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> diff --git a/include/binder/Enum.h b/services/surfaceflinger/WpHash.h index 4c256546f6..86777b71d1 100644 --- a/include/binder/Enum.h +++ b/services/surfaceflinger/WpHash.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -16,7 +16,10 @@ #pragma once -#error Do not rely on global include files. All Android cc_* programs are given access to \ - include_dirs for frameworks/native/include via global configuration, but this is legacy \ - configuration. Instead, you should have a direct dependency on libbinder OR one of your \ - dependencies should re-export libbinder headers with export_shared_lib_headers. +namespace android { + +struct WpHash { + size_t operator()(const wp<IBinder>& p) const { return std::hash<IBinder*>()(p.unsafe_get()); } +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto index e7fb1806be..edeacfa72a 100644 --- a/services/surfaceflinger/layerproto/transactions.proto +++ b/services/surfaceflinger/layerproto/transactions.proto @@ -39,21 +39,28 @@ message TransactionTraceFile { } message TransactionTraceEntry { - int64 elapsed_time = 1; + int64 elapsed_realtime_nanos = 1; int64 vsync_id = 2; repeated TransactionState transactions = 3; + repeated LayerCreationArgs new_layers = 4; + repeated DisplayState new_displays = 5; +} + +message LayerCreationArgs { + int32 layer_id = 1; + string name = 2; + uint32 flags = 3; + int32 parent_id = 4; } message TransactionState { - string tag = 2; - int32 pid = 3; - int32 uid = 4; - int64 vsync_id = 5; - int32 input_event_id = 6; - int64 post_time = 7; - repeated LayerState layer_changes = 9; - repeated DisplayState new_displays = 10; - repeated DisplayState display_changes = 11; + int32 pid = 1; + int32 uid = 2; + int64 vsync_id = 3; + int32 input_event_id = 4; + int64 post_time = 5; + repeated LayerState layer_changes = 6; + repeated DisplayState display_changes = 7; } // Keep insync with layer_state_t @@ -130,8 +137,8 @@ message LayerState { eLayerSecure = 0x80; eEnableBackpressure = 0x100; }; - uint32 flags = 10; - uint32 mask = 11; + uint32 flags = 9; + uint32 mask = 10; message Matrix22 { float dsdx = 1; @@ -139,29 +146,29 @@ message LayerState { float dtdy = 3; float dsdy = 4; }; - Matrix22 matrix = 12; - float corner_radius = 13; - uint32 background_blur_radius = 14; - int32 parent_id = 15; - int32 relative_parent_id = 16; + Matrix22 matrix = 11; + float corner_radius = 12; + uint32 background_blur_radius = 13; + int32 parent_id = 14; + int32 relative_parent_id = 15; - float alpha = 50; + float alpha = 16; message Color3 { float r = 1; float g = 2; float b = 3; } - Color3 color = 18; - RegionProto transparent_region = 19; - uint32 transform = 20; - bool transform_to_display_inverse = 21; - RectProto crop = 49; + Color3 color = 17; + RegionProto transparent_region = 18; + uint32 transform = 19; + bool transform_to_display_inverse = 20; + RectProto crop = 21; message BufferData { uint64 buffer_id = 1; uint32 width = 2; uint32 height = 3; - uint64 frame_number = 5; + uint64 frame_number = 4; enum BufferDataChange { BufferDataChangeNone = 0; @@ -169,14 +176,14 @@ message LayerState { frameNumberChanged = 0x02; cachedBufferChanged = 0x04; } - uint32 flags = 6; - uint64 cached_buffer_id = 7; + uint32 flags = 5; + uint64 cached_buffer_id = 6; } - BufferData buffer_data = 23; - int32 api = 24; - bool has_sideband_stream = 25; - ColorTransformProto color_transform = 26; - repeated BlurRegion blur_regions = 27; + BufferData buffer_data = 22; + int32 api = 23; + bool has_sideband_stream = 24; + ColorTransformProto color_transform = 25; + repeated BlurRegion blur_regions = 26; message Transform { float dsdx = 1; @@ -189,38 +196,38 @@ message LayerState { message WindowInfo { uint32 layout_params_flags = 1; int32 layout_params_type = 2; - RegionProto touchable_region = 4; - int32 surface_inset = 5; - bool focusable = 8; - bool has_wallpaper = 9; - float global_scale_factor = 10; - int32 crop_layer_id = 13; - bool replace_touchable_region_with_crop = 14; - RectProto touchable_region_crop = 15; - Transform transform = 16; + RegionProto touchable_region = 3; + int32 surface_inset = 4; + bool focusable = 5; + bool has_wallpaper = 6; + float global_scale_factor = 7; + int32 crop_layer_id = 8; + bool replace_touchable_region_with_crop = 9; + RectProto touchable_region_crop = 10; + Transform transform = 11; } - WindowInfo window_info_handle = 28; - float bg_color_alpha = 31; - int32 bg_color_dataspace = 32; - bool color_space_agnostic = 33; - float shadow_radius = 34; - int32 frame_rate_selection_priority = 35; - float frame_rate = 36; - int32 frame_rate_compatibility = 37; - int32 change_frame_rate_strategy = 38; - uint32 fixed_transform_hint = 39; - uint64 frame_number = 40; - bool auto_refresh = 41; - bool is_trusted_overlay = 42; - RectProto buffer_crop = 44; - RectProto destination_frame = 45; + WindowInfo window_info_handle = 27; + float bg_color_alpha = 28; + int32 bg_color_dataspace = 29; + bool color_space_agnostic = 30; + float shadow_radius = 31; + int32 frame_rate_selection_priority = 32; + float frame_rate = 33; + int32 frame_rate_compatibility = 34; + int32 change_frame_rate_strategy = 35; + uint32 fixed_transform_hint = 36; + uint64 frame_number = 37; + bool auto_refresh = 38; + bool is_trusted_overlay = 39; + RectProto buffer_crop = 40; + RectProto destination_frame = 41; enum DropInputMode { NONE = 0; ALL = 1; OBSCURED = 2; }; - DropInputMode drop_input_mode = 48; + DropInputMode drop_input_mode = 42; } message DisplayState { diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index 673239dfa9..caeff4ab21 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -63,18 +63,17 @@ static status_t startGraphicsAllocatorService() { return OK; } -static status_t startDisplayService() { +static void startDisplayService() { using android::frameworks::displayservice::V1_0::implementation::DisplayService; using android::frameworks::displayservice::V1_0::IDisplayService; sp<IDisplayService> displayservice = new DisplayService(); status_t err = displayservice->registerAsService(); + // b/141930622 if (err != OK) { - ALOGE("Could not register IDisplayService service."); + ALOGE("Did not register (deprecated) IDisplayService service."); } - - return err; } int main(int, char**) { diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 78f8a2f0ae..7702ea240b 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -414,16 +414,6 @@ prop { prop_name: "ro.surface_flinger.supports_background_blur" } -# Indicates whether Scheduler should use frame rate API when adjusting the -# display refresh rate. -prop { - api_name: "use_frame_rate_api" - type: Boolean - scope: Public - access: Readonly - prop_name: "ro.surface_flinger.use_frame_rate_api" -} - # Sets the timeout used to rate limit DISPLAY_UPDATE_IMMINENT Power HAL notifications. # SurfaceFlinger wakeups will trigger this boost whenever they are separated by more than this # duration (specified in milliseconds). A value of 0 disables the rate limit, and will result in diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 9c567d6afa..bf1e7e2908 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -152,10 +152,6 @@ props { prop_name: "ro.surface_flinger.use_context_priority" } prop { - api_name: "use_frame_rate_api" - prop_name: "ro.surface_flinger.use_frame_rate_api" - } - prop { api_name: "use_smart_90_for_video" prop_name: "ro.surface_flinger.use_smart_90_for_video" deprecated: true diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt index ba60a7defb..640b9fb163 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt @@ -136,10 +136,6 @@ props { prop_name: "ro.surface_flinger.use_context_priority" } prop { - api_name: "use_frame_rate_api" - prop_name: "ro.surface_flinger.use_frame_rate_api" - } - prop { api_name: "use_smart_90_for_video" prop_name: "ro.surface_flinger.use_smart_90_for_video" deprecated: true 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 91a5b528f8..5c16feeda8 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -26,6 +26,7 @@ using namespace std::chrono_literals; namespace android { using android::hardware::graphics::common::V1_1::BufferUsage; +using SCHash = SurfaceComposerClient::SCHash; ::testing::Environment* const binderEnv = ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); @@ -102,6 +103,24 @@ public: } } + static void waitForCommitCallback( + CallbackHelper& helper, + const std::unordered_set<sp<SurfaceControl>, SCHash>& committedSc) { + CallbackData callbackData; + ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData)); + + const auto& surfaceControlStats = callbackData.surfaceControlStats; + + ASSERT_EQ(surfaceControlStats.size(), committedSc.size()) << "wrong number of surfaces"; + + for (const auto& stats : surfaceControlStats) { + ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; + + const auto& expectedSc = committedSc.find(stats.surfaceControl); + ASSERT_NE(expectedSc, committedSc.end()) << "unexpected surface control"; + } + } + DisplayEventReceiver mDisplayEventReceiver; int mEpollFd; @@ -1085,4 +1104,29 @@ TEST_F(LayerCallbackTest, DISABLED_NonBufferLayerStateChanges) { EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); } +TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + sp<SurfaceControl> offscreenLayer = + createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, layer.get()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer, true); + err |= fillTransaction(transaction, &callback, offscreenLayer, true); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.reparent(offscreenLayer, nullptr) + .addTransactionCommittedCallback(callback.function, callback.getContext()); + transaction.apply(); + + std::unordered_set<sp<SurfaceControl>, SCHash> committedSc; + committedSc.insert(layer); + committedSc.insert(offscreenLayer); + EXPECT_NO_FATAL_FAILURE(waitForCommitCallback(callback, committedSc)); +} } // namespace android diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index 3ec6da9ff4..a921aa810e 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/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index 95301b3a37..f9b31853fc 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,18 +518,8 @@ TEST_F(ScreenCaptureTest, CaptureSize) { } TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, - ISurfaceComposerClient::eFXSurfaceBufferState); - - 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); - LayerCaptureArgs args; - args.layerHandle = redLayerHandle; + args.layerHandle = new BBinder(); ScreenCaptureResults captureResults; // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND @@ -840,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 2082c42001..28e8b8c78b 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -754,9 +754,7 @@ void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) { } bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) { - bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment) && - increment.surface_creation().w() == SIZE_UPDATE && - increment.surface_creation().h() == SIZE_UPDATE); + bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment)); if (isMatch && !foundSurface) { foundSurface = true; } else if (isMatch && foundSurface) { diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index f152cedf46..3dc6d8b5c1 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -107,7 +107,6 @@ cc_test { "mock/MockEventThread.cpp", "mock/MockFrameTimeline.cpp", "mock/MockFrameTracer.cpp", - "mock/MockMessageQueue.cpp", "mock/MockNativeWindowSurface.cpp", "mock/MockSurfaceInterceptor.cpp", "mock/MockTimeStats.cpp", @@ -142,6 +141,7 @@ cc_test { "libtimestats", "libtimestats_atoms_proto", "libtimestats_proto", + "libtonemap", "libtrace_proto", "perfetto_trace_protos", ], diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 40ef6e702d..8d2c078305 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -45,7 +45,6 @@ #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" -#include "mock/MockMessageQueue.h" #include "mock/MockTimeStats.h" #include "mock/MockVsyncController.h" #include "mock/system/window/MockNativeWindow.h" @@ -95,7 +94,6 @@ public: ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.mutableEventQueue().reset(mMessageQueue); setupScheduler(); EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) @@ -145,9 +143,6 @@ public: mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), kCallback, kHasMultipleConfigs); - - // Layer history should be created if there are multiple configs. - ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory()); } void setupForceGeometryDirty() { @@ -186,7 +181,6 @@ public: Hwc2::mock::Composer* mComposer = nullptr; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); mock::TimeStats* mTimeStats = new mock::TimeStats(); - mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); Hwc2::mock::PowerAdvisor mPowerAdvisor; sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE; @@ -535,15 +529,16 @@ struct BaseLayerProperties { static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) { // TODO: Eliminate the complexity of actually creating a buffer + layer->setSizeForTest(LayerProperties::WIDTH, LayerProperties::HEIGHT); status_t err = layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT, LayerProperties::FORMAT); ASSERT_EQ(NO_ERROR, err); Mock::VerifyAndClear(test->mRenderEngine); - EXPECT_CALL(*test->mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1); enqueueBuffer(test, layer); - Mock::VerifyAndClearExpectations(test->mMessageQueue); + Mock::VerifyAndClearExpectations(test->mFlinger.scheduler()); bool ignoredRecomputeVisibleRegions; layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0); @@ -838,16 +833,16 @@ template <typename LayerProperties> struct BaseLayerVariant { template <typename L, typename F> static sp<L> createLayerWithFactory(CompositionTest* test, F factory) { - EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(0); + EXPECT_CALL(*test->mFlinger.scheduler(), postMessage(_)).Times(0); sp<L> layer = factory(); // Layer should be registered with scheduler. - EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize()); + EXPECT_EQ(1u, test->mFlinger.scheduler()->layerHistorySize()); Mock::VerifyAndClear(test->mComposer); Mock::VerifyAndClear(test->mRenderEngine); - Mock::VerifyAndClearExpectations(test->mMessageQueue); + Mock::VerifyAndClearExpectations(test->mFlinger.scheduler()); initLayerDrawingStateAndComputeBounds(test, layer); @@ -888,7 +883,7 @@ struct BaseLayerVariant { // Layer should be unregistered with scheduler. test->mFlinger.commit(); - EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize()); + EXPECT_EQ(0u, test->mFlinger.scheduler()->layerHistorySize()); } }; @@ -901,7 +896,6 @@ struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> { FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() { return new EffectLayer( LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer", - LayerProperties::WIDTH, LayerProperties::HEIGHT, LayerProperties::LAYER_FLAGS, LayerMetadata())); }); @@ -940,7 +934,6 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { FlingerLayerType layer = Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() { LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer", - LayerProperties::WIDTH, LayerProperties::HEIGHT, LayerProperties::LAYER_FLAGS, LayerMetadata()); args.textureName = test->mFlinger.mutableTexturePool().back(); return new BufferQueueLayer(args); @@ -952,7 +945,6 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { } static void cleanupInjectedLayers(CompositionTest* test) { - EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(1); Base::cleanupInjectedLayers(test); } @@ -990,7 +982,6 @@ struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> { static FlingerLayerType createLayer(CompositionTest* test) { LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer", - LayerProperties::WIDTH, LayerProperties::HEIGHT, LayerProperties::LAYER_FLAGS, LayerMetadata()); FlingerLayerType layer = new ContainerLayer(args); Base::template initLayerDrawingStateAndComputeBounds(test, layer); diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 6cb3052053..b1f704a48b 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -50,7 +50,6 @@ DisplayTransactionTest::DisplayTransactionTest() { }); injectMockScheduler(); - mFlinger.mutableEventQueue().reset(mMessageQueue); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.mutableInterceptor() = mSurfaceInterceptor; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 7746e73e7a..de5e9dfb97 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -47,7 +47,6 @@ #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" -#include "mock/MockMessageQueue.h" #include "mock/MockNativeWindowSurface.h" #include "mock/MockSchedulerCallback.h" #include "mock/MockSurfaceInterceptor.h" @@ -118,7 +117,6 @@ public: // to keep a reference to them for use in setting up call expectations. renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); Hwc2::mock::Composer* mComposer = nullptr; - mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor; mock::VsyncController* mVsyncController = new mock::VsyncController; diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index cb690aa0e4..67a0d7ec32 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -258,6 +258,15 @@ void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTime << expectedTimestamp; const auto& event = std::get<0>(args.value()); for (int i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) { + auto prediction = + mTokenManager->getPredictionsForToken(event.vsync.frameTimelines[i].vsyncId); + EXPECT_TRUE(prediction.has_value()); + EXPECT_EQ(prediction.value().endTime, event.vsync.frameTimelines[i].deadlineTimestamp) + << "Deadline timestamp does not match cached value"; + EXPECT_EQ(prediction.value().presentTime, + event.vsync.frameTimelines[i].expectedVSyncTimestamp) + << "Expected vsync timestamp does not match cached value"; + if (i > 0) { EXPECT_GT(event.vsync.frameTimelines[i].deadlineTimestamp, event.vsync.frameTimelines[i - 1].deadlineTimestamp) diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp index 010c675574..cd2fc7426e 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -114,8 +114,7 @@ FpsReporterTest::~FpsReporterTest() { sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT, - LAYER_FLAGS, metadata); + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata); return new BufferStateLayer(args); } diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp index 3fa1a2c2f5..d6459429fb 100644 --- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp +++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp @@ -53,7 +53,7 @@ public: sp<BufferStateLayer> createBufferStateLayer() { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0, + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0, LayerMetadata()); return new BufferStateLayer(args); } diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index e8795fea22..4993a2d8bc 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -59,10 +59,8 @@ protected: LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); } - void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); } - - LayerHistory& history() { return *mScheduler->mutableLayerHistory(); } - const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); } + LayerHistory& history() { return mScheduler->mutableLayerHistory(); } + const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); } LayerHistory::Summary summarizeLayerHistory(nsecs_t now) { return history().summarize(*mScheduler->refreshRateConfigs(), now); diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 17d1dd6b09..bd4dc593ba 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -41,7 +41,7 @@ class TestableMessageQueue : public impl::MessageQueue { struct MockHandler : MessageQueue::Handler { using MessageQueue::Handler::Handler; - MOCK_METHOD(void, dispatchCommit, (int64_t, nsecs_t), (override)); + MOCK_METHOD(void, dispatchFrame, (int64_t, nsecs_t), (override)); }; explicit TestableMessageQueue(sp<MockHandler> handler) @@ -96,7 +96,7 @@ TEST_F(MessageQueueTest, commit) { EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit()); + EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); @@ -109,13 +109,13 @@ TEST_F(MessageQueueTest, commitTwice) { .earliestVsync = 0}; EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit()); + EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit()); + EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); @@ -128,7 +128,7 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { .earliestVsync = 0}; EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit()); + EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); @@ -141,7 +141,7 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { generateTokenForPredictions( frametimeline::TimelineItem(startTime, endTime, presentTime))) .WillOnce(Return(vsyncId)); - EXPECT_CALL(*mEventQueue.mHandler, dispatchCommit(vsyncId, presentTime)).Times(1); + EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, presentTime)).Times(1); EXPECT_NO_FATAL_FAILURE(mEventQueue.vsyncCallback(presentTime, startTime, endTime)); EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); @@ -152,7 +152,7 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { .earliestVsync = presentTime}; EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit()); + EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); } TEST_F(MessageQueueTest, commitWithDurationChange) { @@ -164,7 +164,7 @@ TEST_F(MessageQueueTest, commitWithDurationChange) { .earliestVsync = 0}; EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit()); + EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp index e388a6f40f..1e6e3361b2 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp @@ -91,15 +91,14 @@ RefreshRateSelectionTest::~RefreshRateSelectionTest() { sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT, - LAYER_FLAGS, LayerMetadata()); + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS, + LayerMetadata()); return new BufferStateLayer(args); } sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS, - LayerMetadata()); + LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata()); return new EffectLayer(args); } diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 599b235ff2..e558f3b700 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -68,14 +68,6 @@ protected: std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - - // The scheduler should initially disable VSYNC. - struct ExpectDisableVsync { - ExpectDisableVsync(mock::SchedulerCallback& callback) { - EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1); - } - } mExpectDisableVsync{mSchedulerCallback}; - TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback}; Scheduler::ConnectionHandle mConnectionHandle; @@ -166,9 +158,9 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger()); // recordLayerHistory should be a noop - ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers()); + ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers()); + ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); constexpr bool kPowerStateNormal = true; mScheduler->setDisplayPowerState(kPowerStateNormal); @@ -181,17 +173,17 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup } TEST_F(SchedulerTest, updateDisplayModes) { - ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize()); + ASSERT_EQ(0u, mScheduler->layerHistorySize()); sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger()); - ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize()); + ASSERT_EQ(1u, mScheduler->layerHistorySize()); mScheduler->setRefreshRateConfigs( std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId())); - ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers()); + ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers()); + ASSERT_EQ(1u, mScheduler->getNumActiveLayers()); } TEST_F(SchedulerTest, testDispatchCachedReportedMode) { diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index 3b409658d7..eed62a70c3 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -33,7 +33,6 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/MockEventThread.h" -#include "mock/MockMessageQueue.h" #include "mock/MockVsyncController.h" namespace android { @@ -70,8 +69,8 @@ public: std::string name() override { return "BufferStateLayer"; } sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override { sp<Client> client; - LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT, - LAYER_FLAGS, LayerMetadata()); + LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, + LayerMetadata()); return new BufferStateLayer(args); } }; @@ -81,7 +80,7 @@ public: std::string name() override { return "EffectLayer"; } sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override { sp<Client> client; - LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS, + LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata()); return new EffectLayer(args); } @@ -112,7 +111,6 @@ protected: void commitTransaction(); TestableSurfaceFlinger mFlinger; - mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); std::vector<sp<Layer>> mLayers; }; @@ -122,12 +120,9 @@ SetFrameRateTest::SetFrameRateTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.mutableUseFrameRateApi() = true; - setupScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); - mFlinger.mutableEventQueue().reset(mMessageQueue); } void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) { @@ -174,7 +169,7 @@ void SetFrameRateTest::setupScheduler() { namespace { TEST_P(SetFrameRateTest, SetAndGet) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -185,7 +180,7 @@ TEST_P(SetFrameRateTest, SetAndGet) { } TEST_P(SetFrameRateTest, SetAndGetParent) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -210,7 +205,7 @@ TEST_P(SetFrameRateTest, SetAndGetParent) { } TEST_P(SetFrameRateTest, SetAndGetParentAllVote) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -249,7 +244,7 @@ TEST_P(SetFrameRateTest, SetAndGetParentAllVote) { } TEST_P(SetFrameRateTest, SetAndGetChild) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -274,7 +269,7 @@ TEST_P(SetFrameRateTest, SetAndGetChild) { } TEST_P(SetFrameRateTest, SetAndGetChildAllVote) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -313,7 +308,7 @@ TEST_P(SetFrameRateTest, SetAndGetChildAllVote) { } TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -343,7 +338,7 @@ TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) { } TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -374,7 +369,7 @@ TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) { } TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -456,24 +451,20 @@ TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { parent->setFrameRate(FRAME_RATE_VOTE1); commitTransaction(); - mFlinger.mutableScheduler() - .mutableLayerHistory() - ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); - mFlinger.mutableScheduler() - .mutableLayerHistory() - ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); - - const auto layerHistorySummary = - mFlinger.mutableScheduler() - .mutableLayerHistory() - ->summarize(*mFlinger.mutableScheduler().refreshRateConfigs(), 0); - ASSERT_EQ(2u, layerHistorySummary.size()); - EXPECT_EQ(FRAME_RATE_VOTE1.rate, layerHistorySummary[0].desiredRefreshRate); - EXPECT_EQ(FRAME_RATE_VOTE1.rate, layerHistorySummary[1].desiredRefreshRate); + auto& history = mFlinger.mutableScheduler().mutableLayerHistory(); + history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); + history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); + + const auto configs = mFlinger.mutableScheduler().refreshRateConfigs(); + const auto summary = history.summarize(*configs, 0); + + ASSERT_EQ(2u, summary.size()); + EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate); + EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[1].desiredRefreshRate); } TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp index 8c3034178f..f7d34ac5da 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp @@ -52,7 +52,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) { // Cleanup conditions // Creating the display commits a display transaction. - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); } TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { @@ -87,7 +87,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { // Cleanup conditions // Creating the display commits a display transaction. - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp index 7087fb6568..c7e61c96d6 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp @@ -41,7 +41,7 @@ TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) { EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); // Destroying the display commits a display transaction. - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); // -------------------------------------------------------------------- // Invocation diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index 29ff0cd90b..c9a2b00fa3 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -39,7 +39,7 @@ TEST_F(HotplugTest, enqueuesEventsForDisplayTransaction) { // Call Expectations // We expect a scheduled commit for the display transaction. - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); // -------------------------------------------------------------------- // Invocation @@ -86,7 +86,7 @@ TEST_F(HotplugTest, processesEnqueuedEventsIfCalledOnMainThread) { // Call Expectations // We expect a scheduled commit for the display transaction. - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); // -------------------------------------------------------------------- // Invocation diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp index e1b44cf549..37cf05ef64 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp @@ -47,7 +47,7 @@ TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { Case::Display::setupHwcGetActiveConfigCallExpectations(this); // We expect a scheduled commit for the display transaction. - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index 6edebd4b3d..b57feffe83 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -273,7 +273,7 @@ struct DisplayPowerCase { } static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1); } static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 1d21bd43e3..9d1fc981aa 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -30,21 +30,30 @@ namespace android { -class TestableScheduler : public Scheduler { +class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs, + TestableScheduler(std::shared_ptr<scheduler::RefreshRateConfigs> configs, ISchedulerCallback& callback) : TestableScheduler(std::make_unique<mock::VsyncController>(), - std::make_unique<mock::VSyncTracker>(), refreshRateConfigs, + std::make_unique<mock::VSyncTracker>(), std::move(configs), callback) {} TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, - const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs, + std::shared_ptr<scheduler::RefreshRateConfigs> configs, ISchedulerCallback& callback) - : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, - refreshRateConfigs, callback, createLayerHistory(), - {.useContentDetection = true}) {} + : Scheduler(*this, callback, {.useContentDetection = true}) { + mVsyncSchedule = {std::move(vsyncController), std::move(vsyncTracker), nullptr}; + setRefreshRateConfigs(std::move(configs)); + + ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { + // Execute task to prevent broken promise exception on destruction. + handler->handleMessage(Message()); + }); + } + + MOCK_METHOD(void, scheduleFrame, (), (override)); + MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override)); // Used to inject mock event thread. ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { @@ -58,22 +67,13 @@ public: auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; } auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; } - bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); } + auto& mutableLayerHistory() { return mLayerHistory; } - auto* mutableLayerHistory() { return mLayerHistory.get(); } - - size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { - if (!mLayerHistory) return 0; - return mutableLayerHistory()->mLayerInfos.size(); - } + size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mLayerInfos.size(); } + size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayersEnd; } auto refreshRateConfigs() { return holdRefreshRateConfigs(); } - size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS { - if (!mLayerHistory) return 0; - return mutableLayerHistory()->mActiveLayersEnd; - } - void replaceTouchTimer(int64_t millis) { if (mTouchTimer) { mTouchTimer.reset(); @@ -112,6 +112,12 @@ public: mVsyncSchedule.controller.reset(); mConnections.clear(); } + +private: + // ICompositor overrides: + bool commit(nsecs_t, int64_t, nsecs_t) override { return false; } + void composite(nsecs_t) override {} + void sample() override {} }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index c23fcc7d58..4c5789e47f 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -73,20 +73,11 @@ public: return nullptr; } - std::unique_ptr<MessageQueue> createMessageQueue(ICompositor& compositor) override { - return std::make_unique<android::impl::MessageQueue>(compositor); - } - std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps /*currentRefreshRate*/) override { return std::make_unique<scheduler::FakePhaseOffsets>(); } - std::unique_ptr<Scheduler> createScheduler( - const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override { - return nullptr; - } - sp<SurfaceInterceptor> createSurfaceInterceptor() override { return new android::impl::SurfaceInterceptor(); } @@ -375,6 +366,7 @@ public: auto& getTransactionQueue() { return mFlinger->mTransactionQueue; } auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; } + auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; } auto setTransactionState( const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states, @@ -430,7 +422,6 @@ public: auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; } auto& mutableDisplays() { return mFlinger->mDisplays; } auto& mutableDrawingState() { return mFlinger->mDrawingState; } - auto& mutableEventQueue() { return mFlinger->mEventQueue; } auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; } auto& mutableInterceptor() { return mFlinger->mInterceptor; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } @@ -445,7 +436,6 @@ public: auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; } - auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; } auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; } auto fromHandle(const sp<IBinder>& handle) { @@ -460,7 +450,6 @@ public: mutableDisplays().clear(); mutableCurrentState().displays.clear(); mutableDrawingState().displays.clear(); - mutableEventQueue().reset(); mutableInterceptor().clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 05551b4701..ec19100c2b 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -24,12 +24,11 @@ #include <gtest/gtest.h> #include <gui/SurfaceComposerClient.h> #include <log/log.h> +#include <ui/MockFence.h> #include <utils/String8.h> - #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/MockEventThread.h" -#include "mock/MockMessageQueue.h" #include "mock/MockVsyncController.h" namespace android { @@ -46,7 +45,6 @@ public: ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.mutableEventQueue().reset(mMessageQueue); setupScheduler(); } @@ -74,6 +72,13 @@ public: EXPECT_CALL(*mVSyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*mFenceUnsignaled, getStatus()) + .WillRepeatedly(Return(Fence::Status::Unsignaled)); + EXPECT_CALL(*mFenceUnsignaled2, getStatus()) + .WillRepeatedly(Return(Fence::Status::Unsignaled)); + EXPECT_CALL(*mFenceSignaled, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled)); + EXPECT_CALL(*mFenceSignaled2, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled)); + mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController), std::unique_ptr<mock::VSyncTracker>(mVSyncTracker), @@ -85,9 +90,12 @@ public: std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>(); - mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); mock::VsyncController* mVsyncController = new mock::VsyncController(); mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker(); + mock::MockFence* mFenceUnsignaled = new mock::MockFence(); + mock::MockFence* mFenceSignaled = new mock::MockFence(); + mock::MockFence* mFenceUnsignaled2 = new mock::MockFence(); + mock::MockFence* mFenceSignaled2 = new mock::MockFence(); struct TransactionInfo { Vector<ComposerState> states; @@ -124,9 +132,18 @@ public: transaction.frameTimelineInfo = frameTimelineInfo; } + void setupSingleWithComposer(TransactionInfo& transaction, uint32_t flags, + bool syncInputWindows, int64_t desiredPresentTime, + bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo, + const Vector<ComposerState>* states) { + setupSingle(transaction, flags, syncInputWindows, desiredPresentTime, isAutoTimestamp, + frameTimelineInfo); + transaction.states = *states; + } + void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) { ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); TransactionInfo transaction; setupSingle(transaction, flags, syncInputWindows, /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, @@ -156,7 +173,7 @@ public: void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) { ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); // first check will see desired present time has not passed, // but afterwards it will look like the desired present time has passed @@ -187,9 +204,9 @@ public: ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); nsecs_t time = systemTime(); if (!syncInputWindows) { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(2); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2); } else { - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); } // transaction that should go on the pending thread TransactionInfo transactionA; @@ -245,6 +262,188 @@ public: EXPECT_EQ(0u, transactionQueue.size()); } + void Flush_removesUnsignaledFromTheQueue(Vector<ComposerState> state1, + Vector<ComposerState> state2, + bool updateApplyToken = true) { + ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + + TransactionInfo transactionA; + setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state1); + + mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states, + transactionA.displays, transactionA.flags, + transactionA.applyToken, transactionA.inputWindowCommands, + transactionA.desiredPresentTime, transactionA.isAutoTimestamp, + transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transactionA.id); + + TransactionInfo transactionB; + if (updateApplyToken) { + transactionB.applyToken = sp<IBinder>(); + } + setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state2); + mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states, + transactionB.displays, transactionB.flags, + transactionB.applyToken, transactionB.inputWindowCommands, + transactionB.desiredPresentTime, transactionB.isAutoTimestamp, + transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transactionB.id); + + mFlinger.flushTransactionQueues(); + EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size()); + } + + void Flush_removesFromTheQueue(const Vector<ComposerState>& state) { + ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + + TransactionInfo transaction; + setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state); + + mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states, + transaction.displays, transaction.flags, + transaction.applyToken, transaction.inputWindowCommands, + transaction.desiredPresentTime, transaction.isAutoTimestamp, + transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transaction.id); + + mFlinger.flushTransactionQueues(); + EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_EQ(1u, mFlinger.getTransactionCommittedSignals().size()); + } + + void Flush_keepsInTheQueue(const Vector<ComposerState>& state) { + ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + + TransactionInfo transaction; + setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state); + + mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states, + transaction.displays, transaction.flags, + transaction.applyToken, transaction.inputWindowCommands, + transaction.desiredPresentTime, transaction.isAutoTimestamp, + transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transaction.id); + + mFlinger.flushTransactionQueues(); + EXPECT_EQ(1u, mFlinger.getPendingTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size()); + } + + void Flush_KeepsUnsignaledInTheQueue(const Vector<ComposerState>& state1, + const Vector<ComposerState>& state2, + bool updateApplyToken = true, + uint32_t pendingTransactionQueueSize = 1u) { + EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + auto time = systemTime(); + TransactionInfo transactionA; + TransactionInfo transactionB; + setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state1); + setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state2); + mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states, + transactionA.displays, transactionA.flags, + transactionA.applyToken, transactionA.inputWindowCommands, + transactionA.desiredPresentTime, transactionA.isAutoTimestamp, + transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transactionA.id); + if (updateApplyToken) { + transactionB.applyToken = sp<IBinder>(); + } + mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states, + transactionB.displays, transactionB.flags, + transactionB.applyToken, transactionB.inputWindowCommands, + transactionB.desiredPresentTime, transactionB.isAutoTimestamp, + transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transactionB.id); + + mFlinger.flushTransactionQueues(); + EXPECT_EQ(pendingTransactionQueueSize, mFlinger.getPendingTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getTransactionQueue().size()); + } + + void Flush_removesSignaledFromTheQueue(const Vector<ComposerState>& state1, + const Vector<ComposerState>& state2) { + ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + + auto time = systemTime(); + TransactionInfo transactionA; + TransactionInfo transactionB; + setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state1); + setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous, + /*syncInputWindows*/ false, + /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true, + FrameTimelineInfo{}, &state2); + mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states, + transactionA.displays, transactionA.flags, + transactionA.applyToken, transactionA.inputWindowCommands, + transactionA.desiredPresentTime, transactionA.isAutoTimestamp, + transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transactionA.id); + mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states, + transactionB.displays, transactionB.flags, + transactionB.applyToken, transactionB.inputWindowCommands, + transactionB.desiredPresentTime, transactionB.isAutoTimestamp, + transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transactionB.id); + + mFlinger.flushTransactionQueues(); + EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + EXPECT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size()); + } + + static Vector<ComposerState> createComposerStateVector(const ComposerState& state1, + const ComposerState& state2) { + Vector<ComposerState> states; + states.push_back(state1); + states.push_back(state2); + return states; + } + + static Vector<ComposerState> createComposerStateVector(const ComposerState& state) { + Vector<ComposerState> states; + states.push_back(state); + return states; + } + + static ComposerState createComposerState(int layerId, sp<Fence> fence, + uint32_t stateFlags = layer_state_t::eBufferChanged) { + ComposerState composer_state; + composer_state.state.bufferData.acquireFence = std::move(fence); + composer_state.state.layerId = layerId; + composer_state.state.bufferData.flags = BufferData::BufferDataChange::fenceChanged; + composer_state.state.flags = stateFlags; + return composer_state; + } + bool mHasListenerCallbacks = false; std::vector<ListenerCallbacks> mCallbacks; int mTransactionNumber = 0; @@ -252,7 +451,7 @@ public: TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); - EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); TransactionInfo transactionA; // transaction to go on pending queue setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false, @@ -327,4 +526,216 @@ TEST_F(TransactionApplicationTest, FromHandle) { auto ret = mFlinger.fromHandle(badHandle); EXPECT_EQ(nullptr, ret.promote().get()); } + +TEST_F(TransactionApplicationTest, Flush_RemovesSingleSignaledFromTheQueue_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_removesFromTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesSingleUnSignaledFromTheQueue_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_removesFromTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled))); +} + +TEST_F(TransactionApplicationTest, + Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_keepsInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceUnsignaled, layer_state_t::eCropChanged))); +} + +TEST_F(TransactionApplicationTest, + Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_keepsInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceUnsignaled, + layer_state_t::eCropChanged | layer_state_t::eBufferChanged))); +} + +TEST_F(TransactionApplicationTest, + Flush_KeepsInTheQueueSameApplyTokenMultiState_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_keepsInTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled), + createComposerState(/*layerId*/ 1, mFenceSignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_MultipleStateTransaction_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_keepsInTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled), + createComposerState(/*layerId*/ 2, mFenceSignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_removesSignaledFromTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceSignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceSignaled2))); +} + +TEST_F(TransactionApplicationTest, Flush_RemoveSignaledWithUnsignaledIntact_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_KeepsUnsignaledInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceSignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceUnsignaled))); + EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size()); +} + +TEST_F(TransactionApplicationTest, + Flush_KeepsTransactionInTheQueueSameApplyToken_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_KeepsUnsignaledInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceUnsignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceSignaled)), + /*updateApplyToken*/ false); + EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size()); +} + +TEST_F(TransactionApplicationTest, Flush_KeepsTransactionInTheQueue_LatchUnsignaled_Auto) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto; + Flush_KeepsUnsignaledInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceUnsignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceUnsignaled)), + /*updateApplyToken*/ true, + /*pendingTransactionQueueSize*/ 2u); + EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size()); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_removesFromTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_LatchUnsignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_keepsInTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueSameLayerId_LatchUnsignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_keepsInTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled), + createComposerState(/*layerId*/ 1, mFenceUnsignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueDifferentLayerId_LatchUnsignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_keepsInTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled), + createComposerState(/*layerId*/ 2, mFenceUnsignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_removesSignaledFromTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceSignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceSignaled2))); +} + +TEST_F(TransactionApplicationTest, + Flush_KeepInTheQueueDifferentApplyToken_LatchUnsignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_KeepsUnsignaledInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceUnsignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceSignaled))); + EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size()); +} + +TEST_F(TransactionApplicationTest, Flush_KeepInTheQueueSameApplyToken_LatchUnsignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_KeepsUnsignaledInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceSignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceUnsignaled)), + /*updateApplyToken*/ false); + EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size()); +} + +TEST_F(TransactionApplicationTest, Flush_KeepInTheUnsignaledTheQueue_LatchUnsignaled_Disabled) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled; + Flush_KeepsUnsignaledInTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceUnsignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceUnsignaled)), + /*updateApplyToken*/ false); + EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size()); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesFromTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueue_LatchUnsignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesFromTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueueSameLayerId_LatchUnsignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesFromTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled), + createComposerState(/*layerId*/ 1, mFenceSignaled))); +} + +TEST_F(TransactionApplicationTest, + Flush_RemovesFromTheQueueDifferentLayerId_LatchUnsignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesFromTheQueue( + createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled), + createComposerState(/*layerId*/ 2, mFenceSignaled))); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesSignaledFromTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceSignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceSignaled2))); +} + +TEST_F(TransactionApplicationTest, + Flush_RemovesFromTheQueueDifferentApplyToken_LatchUnsignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesUnsignaledFromTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, mFenceSignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, + mFenceUnsignaled))); +} + +TEST_F(TransactionApplicationTest, + Flush_RemovesUnsignaledFromTheQueueSameApplyToken_LatchUnsignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesUnsignaledFromTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, + mFenceUnsignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, mFenceSignaled)), + /*updateApplyToken*/ false); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesUnsignaledFromTheQueue_LatchUnsignaled_Always) { + SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; + Flush_removesUnsignaledFromTheQueue(createComposerStateVector( + createComposerState(/*layerId*/ 1, + mFenceUnsignaled)), + createComposerStateVector( + createComposerState(/*layerId*/ 2, + mFenceUnsignaled))); +} + } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index bd6a7805ec..deeb785bb9 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -57,7 +57,7 @@ public: sp<BufferStateLayer> createBufferStateLayer() { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0, + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0, LayerMetadata()); return new BufferStateLayer(args); } diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp index cebd4519b1..6e00748b45 100644 --- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp @@ -69,16 +69,16 @@ TEST(TransactionProtoParserTest, parse) { t1.displays.add(display); } - std::function<int32_t(const sp<IBinder>&)> getLayerIdFn = [&](const sp<IBinder>& handle) { + TransactionProtoParser::LayerHandleToIdFn getLayerIdFn = [&](const sp<IBinder>& handle) { return (handle == layerHandle) ? 42 : -1; }; - std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn = [&](const sp<IBinder>& handle) { + TransactionProtoParser::DisplayHandleToIdFn getDisplayIdFn = [&](const sp<IBinder>& handle) { return (handle == displayHandle) ? 43 : -1; }; - std::function<sp<IBinder>(int32_t)> getLayerHandleFn = [&](int32_t id) { + TransactionProtoParser::LayerIdToHandleFn getLayerHandleFn = [&](int32_t id) { return (id == 42) ? layerHandle : nullptr; }; - std::function<sp<IBinder>(int32_t)> getDisplayHandleFn = [&](int32_t id) { + TransactionProtoParser::DisplayIdToHandleFn getDisplayHandleFn = [&](int32_t id) { return (id == 43) ? displayHandle : nullptr; }; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index bf69704210..704340deac 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -57,7 +57,7 @@ public: sp<BufferStateLayer> createBufferStateLayer() { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0, + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0, LayerMetadata()); return new BufferStateLayer(args); } diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp index e4f74694f7..15fea9c6b2 100644 --- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp @@ -27,7 +27,6 @@ #include "TunnelModeEnabledReporter.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/MockEventThread.h" -#include "mock/MockMessageQueue.h" namespace android { @@ -69,12 +68,12 @@ protected: TestableSurfaceFlinger mFlinger; Hwc2::mock::Composer* mComposer = nullptr; + sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener = - new TestableTunnelModeEnabledListener(); - sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter = - new TunnelModeEnabledReporter(); + sp<TestableTunnelModeEnabledListener>::make(); - mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); + sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter = + sp<TunnelModeEnabledReporter>::make(); }; TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() { @@ -82,7 +81,6 @@ TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.mutableEventQueue().reset(mMessageQueue); setupScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter; @@ -100,8 +98,7 @@ TunnelModeEnabledReporterTest::~TunnelModeEnabledReporterTest() { sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer( LayerMetadata metadata = {}) { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT, - LAYER_FLAGS, metadata); + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata); return new BufferStateLayer(args); } diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index ba2e4db0fa..8b48e1c16d 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -25,7 +25,7 @@ namespace android::mock { class MockLayer : public Layer { public: MockLayer(SurfaceFlinger* flinger, std::string name) - : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {} + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {} explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {} MOCK_CONST_METHOD0(getType, const char*()); diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp deleted file mode 100644 index 5fb06fd65f..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2018 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 "mock/MockMessageQueue.h" - -namespace android::mock { - -MessageQueue::MessageQueue() { - ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { - // Execute task to prevent broken promise exception on destruction. - handler->handleMessage(Message()); - }); -} - -MessageQueue::~MessageQueue() = default; - -} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h deleted file mode 100644 index d68433760d..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2018 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 <gmock/gmock.h> - -#include "FrameTimeline.h" -#include "Scheduler/EventThread.h" -#include "Scheduler/MessageQueue.h" - -namespace android::mock { - -class MessageQueue : public android::MessageQueue { -public: - MessageQueue(); - ~MessageQueue() override; - - MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>)); - MOCK_METHOD0(waitMessage, void()); - MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&)); - MOCK_METHOD3(initVsync, - void(scheduler::VSyncDispatch&, frametimeline::TokenManager&, - std::chrono::nanoseconds)); - MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration)); - - MOCK_METHOD(void, scheduleCommit, (), (override)); - MOCK_METHOD(void, scheduleComposite, (), (override)); - - MOCK_METHOD(std::optional<Clock::time_point>, getScheduledFrameTime, (), (const, override)); -}; - -} // namespace android::mock 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)); |