Merge "sf-latency: Wait on fence after each drawLayers call"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 157d259..39ef0b5 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -422,9 +422,131 @@
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 @@
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 @@
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 @@
}
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 @@
return ok();
}
-
binder::Status InstalldNativeService::createAppData(
const android::os::CreateAppDataArgs& args,
android::os::CreateAppDataResult* _aidl_return) {
@@ -512,7 +626,7 @@
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 @@
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 @@
}
}
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 @@
}
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;
}
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ae257df..8cfda01 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -47,7 +47,8 @@
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,
diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
index 96d7faa..d5e8ee5 100644
--- a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -23,6 +23,7 @@
int userId;
int flags;
int appId;
+ int previousAppId;
@utf8InCpp String seInfo;
int targetSdkVersion;
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index ea26955..a937436 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -287,6 +287,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_);
@@ -1257,6 +1258,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_));
@@ -1320,6 +1322,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode));
diff --git a/cmds/rss_hwm_reset/rss_hwm_reset.rc b/cmds/rss_hwm_reset/rss_hwm_reset.rc
index fbbc820..271cbf8 100644
--- a/cmds/rss_hwm_reset/rss_hwm_reset.rc
+++ b/cmds/rss_hwm_reset/rss_hwm_reset.rc
@@ -18,7 +18,7 @@
oneshot
user nobody
group nobody readproc
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
capabilities DAC_OVERRIDE
on property:sys.rss_hwm_reset.on=1
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 0b00c2d..fe417a3 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -45,38 +45,14 @@
}
}
-// 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;
int result = 0;
+ /* Strip path off the program name. */
+ char* prog_name = basename(argv[0]);
+
while (1) {
int ic = getopt(argc, argv, "h?");
if (ic < 0)
@@ -88,7 +64,7 @@
wantsUsage = true;
break;
default:
- aerr << "service: Unknown option -" << ic << endl;
+ aerr << prog_name << ": Unknown option -" << ic << endl;
wantsUsage = true;
result = 10;
break;
@@ -103,7 +79,7 @@
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == nullptr) {
- aerr << "service: Unable to get default service manager!" << endl;
+ aerr << prog_name << ": Unable to get default service manager!" << endl;
return 20;
}
@@ -117,7 +93,7 @@
aout << "Service " << argv[optind] <<
(service == nullptr ? ": not found" : ": found") << endl;
} else {
- aerr << "service: No service specified for check" << endl;
+ aerr << prog_name << ": No service specified for check" << endl;
wantsUsage = true;
result = 10;
}
@@ -129,8 +105,8 @@
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) {
@@ -138,7 +114,7 @@
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;
@@ -152,7 +128,7 @@
if (strcmp(argv[optind], "i32") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no integer supplied for 'i32'" << endl;
+ aerr << prog_name << ": no integer supplied for 'i32'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -161,7 +137,7 @@
} else if (strcmp(argv[optind], "i64") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no integer supplied for 'i64'" << endl;
+ aerr << prog_name << ": no integer supplied for 'i64'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -170,7 +146,7 @@
} else if (strcmp(argv[optind], "s16") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no string supplied for 's16'" << endl;
+ aerr << prog_name << ": no string supplied for 's16'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -179,7 +155,7 @@
} else if (strcmp(argv[optind], "f") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no number supplied for 'f'" << endl;
+ aerr << prog_name << ": no number supplied for 'f'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -188,7 +164,7 @@
} else if (strcmp(argv[optind], "d") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no number supplied for 'd'" << endl;
+ aerr << prog_name << ": no number supplied for 'd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -200,7 +176,7 @@
} else if (strcmp(argv[optind], "fd") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no path supplied for 'fd'" << endl;
+ aerr << prog_name << ": no path supplied for 'fd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -208,7 +184,7 @@
const char *path = argv[optind++];
int fd = open(path, O_RDONLY);
if (fd < 0) {
- aerr << "service: could not open '" << path << "'" << endl;
+ aerr << prog_name << ": could not open '" << path << "'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -217,7 +193,7 @@
} else if (strcmp(argv[optind], "afd") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no path supplied for 'afd'" << endl;
+ aerr << prog_name << ": no path supplied for 'afd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -226,7 +202,8 @@
int fd = open(path, O_RDONLY);
struct stat statbuf;
if (fd < 0 || fstat(fd, &statbuf) != 0) {
- aerr << "service: could not open or stat '" << path << "'" << endl;
+ aerr << prog_name << ": could not open or stat"
+ << " '" << path << "'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -240,7 +217,8 @@
} else if (strcmp(argv[optind], "nfd") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no file descriptor supplied for 'nfd'" << endl;
+ aerr << prog_name << ": no file descriptor supplied for"
+ << " 'nfd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -327,7 +305,7 @@
// for now just set the extra field to be null.
data.writeInt32(-1);
} else {
- aerr << "service: unknown option " << argv[optind] << endl;
+ aerr << prog_name << ": unknown option " << argv[optind] << endl;
wantsUsage = true;
result = 10;
break;
@@ -337,44 +315,44 @@
service->transact(code, data, &reply);
aout << "Result: " << reply << endl;
} else {
- aerr << "service: Service " << argv[serviceArg]
+ aerr << prog_name << ": Service " << argv[serviceArg]
<< " does not exist" << endl;
result = 10;
}
} else {
if (optind < argc) {
- aerr << "service: No service specified for call" << endl;
+ aerr << prog_name << ": No service specified for call" << endl;
} else {
- aerr << "service: No code specified for call" << endl;
+ aerr << prog_name << ": No code specified for call" << endl;
}
wantsUsage = true;
result = 10;
}
} else {
- aerr << "service: Unknown command " << argv[optind] << endl;
+ aerr << prog_name << ": Unknown command " << argv[optind] << endl;
wantsUsage = true;
result = 10;
}
}
if (wantsUsage) {
- aout << "Usage: service [-h|-?]\n"
- " service list\n"
- " service check SERVICE\n"
- " service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR | null"
- " | fd f | nfd n | afd f ] ...\n"
+ aout << "Usage: " << prog_name << " [-h|-?]\n"
+ " " << prog_name << " list\n"
+ " " << prog_name << " check SERVICE\n"
+ " " << prog_name << " call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR"
+ " | null | fd f | nfd n | afd f ] ...\n"
"Options:\n"
" i32: Write the 32-bit integer N into the send parcel.\n"
" i64: Write the 64-bit integer N into the send parcel.\n"
- " f: Write the 32-bit single-precision number N into the send parcel.\n"
- " d: Write the 64-bit double-precision number N into the send parcel.\n"
+ " f: Write the 32-bit single-precision number N into the send parcel.\n"
+ " d: Write the 64-bit double-precision number N into the send parcel.\n"
" s16: Write the UTF-16 string STR into the send parcel.\n"
" null: Write a null binder into the send parcel.\n"
- " fd: Write a file descriptor for the file f to the send parcel.\n"
- " nfd: Write file descriptor n to the send parcel.\n"
- " afd: Write an ashmem file descriptor for a region containing the data from"
- " file f to the send parcel.\n";
-// " intent: Write and Intent int the send parcel. ARGS can be\n"
+ " fd: Write a file descriptor for the file f into the send parcel.\n"
+ " nfd: Write the file descriptor n into the send parcel.\n"
+ " afd: Write an ashmem file descriptor for a region containing the data from\n"
+ " file f into the send parcel.\n";
+// " intent: Write an Intent into the send parcel. ARGS can be\n"
// " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n";
return result;
}
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 6d5070f..0dd29e0 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -9,5 +9,5 @@
onrestart class_restart main
onrestart class_restart hal
onrestart class_restart early_hal
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
shutdown critical
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
index 756f6c3..c9305a1 100644
--- a/cmds/servicemanager/vndservicemanager.rc
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -2,7 +2,7 @@
class core
user system
group system readproc
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
onrestart class_restart main
onrestart class_restart hal
onrestart class_restart early_hal
diff --git a/include/android/input.h b/include/android/input.h
index 6d2c1b3..27587ce 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -1385,6 +1385,14 @@
*/
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/binder/Enum.h b/include/binder/Enum.h
deleted file mode 100644
index 4c25654..0000000
--- a/include/binder/Enum.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2020 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
-
-#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.
diff --git a/include/input/Input.h b/include/input/Input.h
index 5015e68..7cc595a 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -801,7 +801,13 @@
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&);
protected:
int32_t mAction;
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index b74923c..ac3e5b8 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -42,12 +42,13 @@
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/binder/Android.bp b/libs/binder/Android.bp
index 2ecb895..8270ae5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -25,6 +25,7 @@
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 @@
vndk: {
enabled: true,
},
+ recovery_available: true,
double_loadable: true,
host_supported: true,
// TODO(b/153609531): remove when no longer needed.
@@ -147,6 +149,11 @@
"UtilsHost.cpp",
],
},
+ recovery: {
+ exclude_header_libs: [
+ "libandroid_runtime_vm_headers",
+ ],
+ },
},
aidl: {
@@ -331,6 +338,7 @@
"libbase",
"libbinder",
"libbinder_ndk",
+ "liblog",
"libutils",
],
export_include_dirs: ["include_rpc_unstable"],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index d3eef4e..ec9d554 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -547,7 +547,6 @@
AutoMutex _l(e->mLock);
auto rpcServer = RpcServer::make();
LOG_ALWAYS_FATAL_IF(rpcServer == nullptr, "RpcServer::make returns null");
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
auto link = sp<RpcServerLink>::make(rpcServer, keepAliveBinder, weakThis);
if (auto status = keepAliveBinder->linkToDeath(link, nullptr, 0); status != OK) {
ALOGE("%s: keepAliveBinder->linkToDeath returns %s", __PRETTY_FUNCTION__,
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index 1c8bdea..f954e74 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 181f405..745d9e9 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -521,6 +521,25 @@
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 @@
#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 @@
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/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 4edc202..93ed50e 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -58,10 +58,6 @@
return sp<RpcServer>::make(std::move(ctx));
}
-void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
- mAgreedExperimental = true;
-}
-
status_t RpcServer::setupUnixDomainServer(const char* path) {
return setupSocketServer(UnixSocketAddress(path));
}
@@ -163,14 +159,12 @@
}
void RpcServer::start() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!");
mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
}
void RpcServer::join() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
{
std::lock_guard<std::mutex> _l(mLock);
@@ -274,9 +268,6 @@
void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
const sockaddr_storage addr, socklen_t addrLen) {
- // TODO(b/183988761): cannot trust this simple ID
- LOG_ALWAYS_FATAL_IF(!server->mAgreedExperimental, "no!");
-
// mShutdownTrigger can only be cleared once connection threads have joined.
// It must be set before this thread is started
LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr);
@@ -506,19 +497,16 @@
}
bool RpcServer::hasServer() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
return mServer.ok();
}
unique_fd RpcServer::releaseServer() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
return std::move(mServer);
}
status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
if (mServer.ok()) {
ALOGE("Each RpcServer can only have one server.");
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 137411b..a5a2bb1 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 @@
}
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 @@
return fn();
}
};
+#endif
} // namespace
void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult) {
@@ -374,7 +381,7 @@
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);
@@ -688,7 +695,8 @@
status_t status = OK;
if (init) {
- mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this));
+ status =
+ mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this));
}
{
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 50de22b..dba0a43 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -60,20 +60,21 @@
RpcState();
~RpcState();
- status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, uint32_t* version);
- status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session);
- status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session);
+ [[nodiscard]] status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, uint32_t* version);
+ [[nodiscard]] status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session);
+ [[nodiscard]] status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session);
// TODO(b/182940634): combine some special transactions into one "getServerInfo" call?
sp<IBinder> getRootObject(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session);
- status_t getMaxThreads(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, size_t* maxThreadsOut);
- status_t getSessionId(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, std::vector<uint8_t>* sessionIdOut);
+ [[nodiscard]] status_t getMaxThreads(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, size_t* maxThreadsOut);
+ [[nodiscard]] status_t getSessionId(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session,
+ std::vector<uint8_t>* sessionIdOut);
[[nodiscard]] status_t transact(const sp<RpcSession::RpcConnection>& connection,
const sp<IBinder>& address, uint32_t code, const Parcel& data,
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index ece7989..bffab5e 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -27,4 +27,7 @@
@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 ff90b30..7d14315 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -129,48 +129,50 @@
#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 d90e803..9670d7b 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -81,6 +81,8 @@
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 @@
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 @@
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/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 04c6b59..aaa812b 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -25,10 +25,6 @@
#include <mutex>
#include <thread>
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
namespace android {
class FdTrigger;
@@ -99,8 +95,6 @@
*/
[[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
- void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
/**
* This must be called before adding a client session.
*
@@ -185,10 +179,9 @@
static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
const sockaddr_storage addr, socklen_t addrLen);
- status_t setupSocketServer(const RpcSocketAddress& address);
+ [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
const std::unique_ptr<RpcTransportCtx> mCtx;
- bool mAgreedExperimental = false;
size_t mMaxThreads = 1;
std::optional<uint32_t> mProtocolVersion;
base::unique_fd mServer; // socket we are accepting sessions on
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index f9b733d..1bc8464 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -26,10 +26,6 @@
#include <thread>
#include <vector>
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
namespace android {
class Parcel;
@@ -140,7 +136,7 @@
* Query the other side of the session for the maximum number of threads
* it supports (maximum number of concurrent non-nested synchronous transactions)
*/
- status_t getRemoteMaxThreads(size_t* maxThreads);
+ [[nodiscard]] status_t getRemoteMaxThreads(size_t* maxThreads);
/**
* See RpcTransportCtx::getCertificate
@@ -220,7 +216,7 @@
bool allowNested = false;
};
- status_t readId();
+ [[nodiscard]] status_t readId();
// A thread joining a server must always call these functions in order, and
// cleanup is only programmed once into join. These are in separate
@@ -262,7 +258,7 @@
std::unique_ptr<RpcTransport> rpcTransport);
[[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
- status_t initShutdownTrigger();
+ [[nodiscard]] status_t initShutdownTrigger();
enum class ConnectionUse {
CLIENT,
@@ -273,8 +269,8 @@
// Object representing exclusive access to a connection.
class ExclusiveConnection {
public:
- static status_t find(const sp<RpcSession>& session, ConnectionUse use,
- ExclusiveConnection* connection);
+ [[nodiscard]] static status_t find(const sp<RpcSession>& session, ConnectionUse use,
+ ExclusiveConnection* connection);
~ExclusiveConnection();
const sp<RpcConnection>& get() { return mConnection; }
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 08f5eed..34f1cbf 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -16,6 +16,8 @@
#pragma once
+#include <sys/socket.h>
+
extern "C" {
struct AIBinder;
@@ -30,6 +32,12 @@
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.
+bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port);
+
AIBinder* RpcClient(unsigned int cid, unsigned int port);
// Connect to an RPC server with preconnected file descriptors.
diff --git a/libs/binder/include_tls/binder/RpcAuth.h b/libs/binder/include_tls/binder/RpcAuth.h
index 4c2f296..ab64828 100644
--- a/libs/binder/include_tls/binder/RpcAuth.h
+++ b/libs/binder/include_tls/binder/RpcAuth.h
@@ -40,7 +40,7 @@
// - SSL_CTX_use_certificate
// - SSL_CTX_set*_chain
// - SSL_CTX_add0_chain_cert
- virtual status_t configure(SSL_CTX* ctx) = 0;
+ [[nodiscard]] virtual status_t configure(SSL_CTX* ctx) = 0;
};
} // namespace android
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index cad55fb..bf2b25b 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -19,6 +19,7 @@
#include <android/binder_libbinder.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+#include <linux/vm_sockets.h>
using android::OK;
using android::RpcServer;
@@ -29,10 +30,31 @@
extern "C" {
+bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port) {
+ auto server = RpcServer::make();
+ if (status_t status = server->setupVsockServer(port); status != OK) {
+ LOG(ERROR) << "Failed to set up vsock server with port " << port
+ << " error: " << statusToString(status).c_str();
+ return false;
+ }
+ server->setPerSessionRootObject([=](const sockaddr* addr, socklen_t addrlen) {
+ LOG_ALWAYS_FATAL_IF(addr->sa_family != AF_VSOCK, "address is not a vsock");
+ LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
+ const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
+ return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
+ });
+
+ server->join();
+
+ // Shutdown any open sessions since server failed.
+ (void)server->shutdown();
+ return true;
+}
+
bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
void* param) {
auto server = RpcServer::make();
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
if (status_t status = server->setupVsockServer(port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
<< " error: " << statusToString(status).c_str();
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 9c04e58..ee46fcb 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -54,6 +54,7 @@
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ recovery_available: true,
llndk: {
symbol_file: "libbinder_ndk.map.txt",
@@ -155,6 +156,7 @@
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 81aa551..6949c2c 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -104,6 +104,17 @@
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 @@
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 @@
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 2b18a0a..67623a6 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 @@
*/
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 @@
*/
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) {
+ 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);
}
- 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
+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);
}
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
- }
- 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;
+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);
}
- if (null == 0) {
- *p = std::nullopt;
- return STATUS_OK;
- }
- *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;
- }
- *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 782328d..4163897 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 @@
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 64170af..d63a8d0 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -145,6 +145,7 @@
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 ecb044e..d323022 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -33,6 +33,27 @@
}
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,
+ }
+ },
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ "com.android.virt",
+ ],
+}
+
+rust_library {
name: "libbinder_ndk_sys",
crate_name: "binder_ndk_sys",
srcs: [
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
new file mode 100644
index 0000000..64833b6
--- /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 854b1f9..4e048d7 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::Parcel;
+use crate::parcel::{OwnedParcel, Parcel};
use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
use crate::sys;
@@ -177,25 +177,25 @@
fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
/// Create a Parcel that can be used with `submit_transact`.
- fn prepare_transact(&self) -> Result<Parcel>;
+ fn prepare_transact(&self) -> Result<OwnedParcel>;
/// Perform a generic operation with the object.
///
- /// The provided [`Parcel`] must have been created by a call to
+ /// The provided [`OwnedParcel`] must have been created by a call to
/// `prepare_transact` on the same binder.
///
/// # Arguments
///
/// * `code` - Transaction code for the operation.
- /// * `data` - [`Parcel`] with input data.
+ /// * `data` - [`OwnedParcel`] with input data.
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)).
fn submit_transact(
&self,
code: TransactionCode,
- data: Parcel,
+ data: OwnedParcel,
flags: TransactionFlags,
- ) -> Result<Parcel>;
+ ) -> Result<OwnedParcel>;
/// Perform a generic operation with the object. This is a convenience
/// method that internally calls `prepare_transact` followed by
@@ -213,8 +213,8 @@
input_callback: F,
) -> Result<Parcel> {
let mut parcel = self.prepare_transact()?;
- input_callback(&mut parcel)?;
- self.submit_transact(code, parcel, flags)
+ input_callback(&mut parcel.borrowed())?;
+ self.submit_transact(code, parcel, flags).map(OwnedParcel::into_parcel)
}
}
@@ -713,12 +713,14 @@
$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 +730,7 @@
$interface:path[$descriptor:expr] {
native: $native:ident($on_transact:path),
proxy: $proxy:ident,
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -735,6 +738,7 @@
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -746,6 +750,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
}
} => {
$crate::declare_binder_interface! {
@@ -754,6 +759,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $crate::Stability::default(),
}
}
@@ -765,6 +771,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -776,6 +783,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -791,6 +799,8 @@
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $( async: $async_interface:ident, )?
+
stability: $stability:expr,
}
} => {
@@ -924,7 +934,7 @@
}
}
- 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 +948,73 @@
.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::Parcel) -> $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::Parcel) -> $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)))
+ }
+ }
+ )?
};
}
diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs
new file mode 100644
index 0000000..214c0b5
--- /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 d1d37d7..2ac2d2f 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -98,6 +98,7 @@
#[macro_use]
mod binder;
+mod binder_async;
mod error;
mod native;
mod state;
@@ -111,9 +112,10 @@
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::Parcel;
+pub use parcel::{OwnedParcel, 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 @@
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/parcel.rs b/libs/binder/rust/src/parcel.rs
index 7391561..a0e1478 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -23,6 +23,7 @@
use std::cell::RefCell;
use std::convert::TryInto;
+use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ptr;
use std::fmt;
@@ -52,6 +53,119 @@
Borrowed(*mut sys::AParcel),
}
+/// A variant of Parcel that is known to be owned.
+pub struct OwnedParcel {
+ ptr: *mut 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
+/// threads.
+unsafe impl Send for OwnedParcel {}
+
+/// A variant of Parcel that is known to be borrowed.
+pub struct BorrowedParcel<'a> {
+ inner: Parcel,
+ _lifetime: PhantomData<&'a mut Parcel>,
+}
+
+impl OwnedParcel {
+ /// Create a new empty `OwnedParcel`.
+ pub fn new() -> OwnedParcel {
+ 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 }
+ }
+
+ /// Convert the provided parcel to an owned parcel, or return `None` if it
+ /// is borrowed.
+ pub fn try_from(parcel: Parcel) -> Option<OwnedParcel> {
+ match &parcel {
+ Parcel::Owned(ptr) => {
+ let ptr = *ptr;
+ std::mem::forget(parcel);
+ Some(OwnedParcel { ptr })
+ }
+ Parcel::Borrowed(_) => None,
+ }
+ }
+
+ /// Create an owned 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. The
+ /// parcel object must be owned by the caller prior to this call, as this
+ /// constructor takes ownership of the parcel and will destroy it on drop.
+ ///
+ /// 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`.
+ ///
+ /// 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 })
+ }
+
+ /// Consume the parcel, transferring ownership to the caller.
+ pub(crate) fn into_raw(self) -> *mut sys::AParcel {
+ let ptr = self.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<'_> {
+ BorrowedParcel {
+ inner: Parcel::Borrowed(self.ptr),
+ _lifetime: PhantomData,
+ }
+ }
+}
+
+impl Default for OwnedParcel {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Clone for OwnedParcel {
+ fn clone(&self) -> Self {
+ let mut new_parcel = Self::new();
+ new_parcel
+ .borrowed()
+ .append_all_from(&Parcel::Borrowed(self.ptr))
+ .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> std::ops::DerefMut for BorrowedParcel<'a> {
+ fn deref_mut(&mut self) -> &mut Parcel {
+ &mut self.inner
+ }
+}
+
/// # Safety
///
/// The `Parcel` constructors guarantee that a `Parcel` object will always
@@ -95,33 +209,6 @@
pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> {
ptr.as_mut().map(|ptr| Self::Borrowed(ptr))
}
-
- /// Create an owned 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. The
- /// parcel object must be owned by the caller prior to this call, as this
- /// constructor takes ownership of the parcel and will destroy it on drop.
- pub(crate) unsafe fn owned(ptr: *mut sys::AParcel) -> Option<Parcel> {
- ptr.as_mut().map(|ptr| Self::Owned(ptr))
- }
-
- /// Consume the parcel, transferring ownership to the caller if the parcel
- /// was owned.
- pub(crate) fn into_raw(mut self) -> *mut sys::AParcel {
- let ptr = self.as_native_mut();
- let _ = ManuallyDrop::new(self);
- ptr
- }
-
- pub(crate) fn is_owned(&self) -> bool {
- match *self {
- Self::Owned(_) => true,
- Self::Borrowed(_) => false,
- }
- }
}
impl Default for Parcel {
@@ -478,6 +565,18 @@
}
}
+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
+ // `AParcel`. Since we own the parcel, we can safely delete it
+ // here.
+ sys::AParcel_delete(self.ptr)
+ }
+ }
+}
+
impl fmt::Debug for Parcel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Parcel")
@@ -485,6 +584,13 @@
}
}
+impl fmt::Debug for OwnedParcel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("OwnedParcel")
+ .finish()
+ }
+}
+
#[test]
fn test_read_write() {
let mut parcel = Parcel::new();
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index ec00e1d..db9d8b0 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -702,6 +702,8 @@
}
}
+impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {}
+
impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
fn deserialize(parcel: &Parcel) -> Result<Self> {
let ibinder: SpIBinder = parcel.read()?;
@@ -716,6 +718,8 @@
}
}
+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<()> {
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index 3e75d1b..bccfd2d 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -16,13 +16,12 @@
use crate::binder::Stability;
use crate::error::{Result, StatusCode};
-use crate::parcel::{Parcel, Parcelable};
+use crate::parcel::{OwnedParcel, Parcel, Parcelable};
use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
-use downcast_rs::{impl_downcast, Downcast};
+use downcast_rs::{impl_downcast, DowncastSync};
use std::any::Any;
-use std::cell::RefCell;
-use std::rc::Rc;
+use std::sync::{Arc, Mutex};
/// Metadata that `ParcelableHolder` needs for all parcelables.
///
@@ -40,18 +39,18 @@
}
}
-trait AnyParcelable: Downcast + Parcelable + std::fmt::Debug {}
-impl_downcast!(AnyParcelable);
-impl<T> AnyParcelable for T where T: Downcast + Parcelable + std::fmt::Debug {}
+trait AnyParcelable: DowncastSync + Parcelable + std::fmt::Debug {}
+impl_downcast!(sync AnyParcelable);
+impl<T> AnyParcelable for T where T: DowncastSync + Parcelable + std::fmt::Debug {}
#[derive(Debug, Clone)]
enum ParcelableHolderData {
Empty,
Parcelable {
- parcelable: Rc<dyn AnyParcelable>,
+ parcelable: Arc<dyn AnyParcelable>,
name: String,
},
- Parcel(Parcel),
+ Parcel(OwnedParcel),
}
impl Default for ParcelableHolderData {
@@ -67,15 +66,15 @@
/// `ParcelableHolder` is currently not thread-safe (neither
/// `Send` nor `Sync`), mainly because it internally contains
/// a `Parcel` which in turn is not thread-safe.
-#[derive(Debug, Default, Clone)]
+#[derive(Debug, Default)]
pub struct ParcelableHolder {
- // This is a `RefCell` because of `get_parcelable`
+ // This is a `Mutex` because of `get_parcelable`
// which takes `&self` for consistency with C++.
// We could make `get_parcelable` take a `&mut self`
- // and get rid of the `RefCell` here for a performance
+ // and get rid of the `Mutex` here for a performance
// improvement, but then callers would require a mutable
// `ParcelableHolder` even for that getter method.
- data: RefCell<ParcelableHolderData>,
+ data: Mutex<ParcelableHolderData>,
stability: Stability,
}
@@ -83,7 +82,7 @@
/// Construct a new `ParcelableHolder` with the given stability.
pub fn new(stability: Stability) -> Self {
Self {
- data: RefCell::new(ParcelableHolderData::Empty),
+ data: Mutex::new(ParcelableHolderData::Empty),
stability,
}
}
@@ -93,20 +92,20 @@
/// Note that this method does not reset the stability,
/// only the contents.
pub fn reset(&mut self) {
- *self.data.get_mut() = ParcelableHolderData::Empty;
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Empty;
// We could also clear stability here, but C++ doesn't
}
/// Set the parcelable contained in this `ParcelableHolder`.
- pub fn set_parcelable<T>(&mut self, p: Rc<T>) -> Result<()>
+ pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<()>
where
- T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug,
+ T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
{
if self.stability > p.get_stability() {
return Err(StatusCode::BAD_VALUE);
}
- *self.data.get_mut() = ParcelableHolderData::Parcelable {
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Parcelable {
parcelable: p,
name: T::get_descriptor().into(),
};
@@ -127,12 +126,12 @@
/// * `Ok(None)` if the holder is empty or the descriptor does not match
/// * `Ok(Some(_))` if the object holds a parcelable of type `T`
/// with the correct descriptor
- pub fn get_parcelable<T>(&self) -> Result<Option<Rc<T>>>
+ pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>>
where
- T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug,
+ T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync,
{
let parcelable_desc = T::get_descriptor();
- let mut data = self.data.borrow_mut();
+ let mut data = self.data.lock().unwrap();
match *data {
ParcelableHolderData::Empty => Ok(None),
ParcelableHolderData::Parcelable {
@@ -143,12 +142,13 @@
return Err(StatusCode::BAD_VALUE);
}
- match Rc::clone(parcelable).downcast_rc::<T>() {
+ match Arc::clone(parcelable).downcast_arc::<T>() {
Err(_) => Err(StatusCode::BAD_VALUE),
Ok(x) => Ok(Some(x)),
}
}
- ParcelableHolderData::Parcel(ref parcel) => {
+ ParcelableHolderData::Parcel(ref mut parcel) => {
+ let parcel = parcel.borrowed();
unsafe {
// Safety: 0 should always be a valid position.
parcel.set_data_position(0)?;
@@ -160,10 +160,10 @@
}
let mut parcelable = T::default();
- parcelable.read_from_parcel(parcel)?;
+ parcelable.read_from_parcel(&parcel)?;
- let parcelable = Rc::new(parcelable);
- let result = Rc::clone(&parcelable);
+ let parcelable = Arc::new(parcelable);
+ let result = Arc::clone(&parcelable);
*data = ParcelableHolderData::Parcelable { parcelable, name };
Ok(Some(result))
@@ -184,7 +184,8 @@
fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> {
parcel.write(&self.stability)?;
- match *self.data.borrow() {
+ let mut data = self.data.lock().unwrap();
+ match *data {
ParcelableHolderData::Empty => parcel.write(&0i32),
ParcelableHolderData::Parcelable {
ref parcelable,
@@ -212,9 +213,10 @@
Ok(())
}
- ParcelableHolderData::Parcel(ref p) => {
+ 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)
}
}
}
@@ -229,7 +231,7 @@
return Err(StatusCode::BAD_VALUE);
}
if data_size == 0 {
- *self.data.get_mut() = ParcelableHolderData::Empty;
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Empty;
return Ok(());
}
@@ -240,9 +242,11 @@
.checked_add(data_size)
.ok_or(StatusCode::BAD_VALUE)?;
- let mut new_parcel = Parcel::new();
- new_parcel.append_from(parcel, data_start, data_size)?;
- *self.data.get_mut() = ParcelableHolderData::Parcel(new_parcel);
+ let mut new_parcel = OwnedParcel::new();
+ new_parcel
+ .borrowed()
+ .append_from(parcel, data_start, data_size)?;
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel);
unsafe {
// Safety: `append_from` checks if `data_size` overflows
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 2c5b0a8..a8d0c33 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -22,7 +22,7 @@
};
use crate::error::{status_result, Result, StatusCode};
use crate::parcel::{
- Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ Deserialize, DeserializeArray, DeserializeOption, OwnedParcel, Parcel, Serialize, SerializeArray,
SerializeOption,
};
use crate::sys;
@@ -235,7 +235,7 @@
}
impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
- fn prepare_transact(&self) -> Result<Parcel> {
+ fn prepare_transact(&self) -> Result<OwnedParcel> {
let mut input = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -253,20 +253,19 @@
unsafe {
// Safety: At this point, `input` is either a valid, owned `AParcel`
- // pointer, or null. `Parcel::owned` safely handles both cases,
+ // pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
// taking ownership of the parcel.
- Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ OwnedParcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
fn submit_transact(
&self,
code: TransactionCode,
- data: Parcel,
+ data: OwnedParcel,
flags: TransactionFlags,
- ) -> Result<Parcel> {
+ ) -> Result<OwnedParcel> {
let mut reply = ptr::null_mut();
- assert!(data.is_owned());
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. Although `IBinder::transact` is
@@ -299,9 +298,8 @@
// after the call to `AIBinder_transact` above, so we can
// construct a `Parcel` out of it. `AIBinder_transact` passes
// ownership of the `reply` parcel to Rust, so we need to
- // construct an owned variant. `Parcel::owned` takes ownership
- // of the parcel pointer.
- Parcel::owned(reply).ok_or(StatusCode::UNEXPECTED_NULL)
+ // construct an owned variant.
+ OwnedParcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 0e05f10..0aef744 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -99,6 +99,17 @@
}
}
+ /// 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 ecc61f4..2d1175b 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -13,6 +13,8 @@
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 335e8d8..ebfe879 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::{Parcel, OwnedParcel};
use binder::{
Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
FIRST_CALL_TRANSACTION,
@@ -154,12 +154,25 @@
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,
}
}
@@ -201,6 +214,32 @@
}
}
+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(())).map(|p| OwnedParcel::try_from(p).unwrap()),
+ |reply| async move { reply?.into_parcel().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(())).map(|p| OwnedParcel::try_from(p).unwrap()),
+ |reply| async move { reply?.into_parcel().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(())).map(|p| OwnedParcel::try_from(p).unwrap()),
+ |reply| async move { reply?.into_parcel().read() }
+ )
+ }
+}
+
impl ITest for Binder<BnTest> {
fn test(&self) -> binder::Result<String> {
self.0.test()
@@ -215,6 +254,23 @@
}
}
+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 {}
@@ -255,7 +311,9 @@
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 @@
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 @@
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 @@
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 @@
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/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 23e34aa..777f3c9 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -90,7 +90,6 @@
LOG(ERROR) << "Cannot create RpcServer";
return EX_SOFTWARE;
}
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
LOG(ERROR) << "setupInetServer failed: " << statusToString(status);
@@ -207,7 +206,6 @@
service = ServiceManagerProxyToNative::asBinder(interface);
auto rpcServer = RpcServer::make();
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
rpcServer->setRootObject(service);
unsigned int port;
if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index c41f424..63a4b2c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -112,7 +112,7 @@
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 @@
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();
@@ -1194,7 +1234,6 @@
auto rpcServer = RpcServer::make();
EXPECT_NE(nullptr, rpcServer);
if (rpcServer == nullptr) return {};
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
if (status_t status = rpcServer->setupInetServer("127.0.0.1", 0, &port); status != OK) {
ADD_FAILURE() << "setupInetServer failed" << statusToString(status);
@@ -1567,7 +1606,7 @@
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/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index f8718aa..52ba9b0 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -206,7 +206,6 @@
if (0 == fork()) {
prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
server->setRootObject(sp<MyBinderRpcBenchmark>::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK_EQ(OK, server->setupUnixDomainServer(addr));
server->join();
exit(1);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 62215bb..55ad3c6 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -109,7 +109,6 @@
base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
int sinkFd = sink.get();
auto server = RpcServer::make(newFactory(GetParam()));
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
ASSERT_FALSE(server->hasServer());
ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
ASSERT_TRUE(server->hasServer());
@@ -543,7 +542,6 @@
auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
server->setMaxThreads(options.numThreads);
unsigned int outPort = 0;
@@ -665,13 +663,15 @@
break;
case AF_INET:
CHECK_EQ(len, sizeof(sockaddr_in));
- service->port = reinterpret_cast<const sockaddr_in*>(addr)
- ->sin_port;
+ service->port =
+ ntohs(reinterpret_cast<const sockaddr_in*>(addr)
+ ->sin_port);
break;
case AF_INET6:
CHECK_EQ(len, sizeof(sockaddr_in));
- service->port = reinterpret_cast<const sockaddr_in6*>(addr)
- ->sin6_port;
+ service->port =
+ ntohs(reinterpret_cast<const sockaddr_in6*>(addr)
+ ->sin6_port);
break;
default:
LOG_ALWAYS_FATAL("Unrecognized address family %d",
@@ -1303,11 +1303,20 @@
}
TEST_P(BinderRpc, UseKernelBinderCallingId) {
+ bool okToFork = ProcessState::selfOrNull() == nullptr;
+
auto proc = createRpcTestSocketServerProcess({});
- // we can't allocate IPCThreadState so actually the first time should
- // succeed :(
- EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+ // If this process has used ProcessState already, then the forked process
+ // cannot use it at all. If this process hasn't used it (depending on the
+ // order tests are run), then the forked process can use it, and we'll only
+ // catch the invalid usage the second time. Such is the burden of global
+ // state!
+ if (okToFork) {
+ // we can't allocate IPCThreadState so actually the first time should
+ // succeed :(
+ EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+ }
// second time! we catch the error :)
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
@@ -1373,7 +1382,6 @@
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
if (status_t status = server->setupVsockServer(vsockPort); status != OK) {
if (status == -EAFNOSUPPORT) {
return false;
@@ -1462,7 +1470,6 @@
TEST_P(BinderRpcSimple, Shutdown) {
auto addr = allocateSocketAddress();
auto server = RpcServer::make(newFactory(GetParam()));
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
auto joinEnds = std::make_shared<OneOffSignal>();
@@ -1502,7 +1509,6 @@
ASSERT_EQ(OK, binder->pingBinder());
auto rpcServer = RpcServer::make();
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
ASSERT_EQ(OK, rpcServer->setupInetServer(kLocalInetAddress, 0, &port));
auto socket = rpcServer->releaseServer();
@@ -1541,7 +1547,6 @@
std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
auto [socketType, rpcSecurity, certificateFormat] = param;
auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
switch (socketType) {
case SocketType::PRECONNECTED: {
return AssertionFailure() << "Not supported by this test";
@@ -2003,5 +2008,6 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+
return RUN_ALL_TESTS();
}
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index e4f57b0..32406e5 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -192,6 +192,8 @@
// 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 @@
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 c0a762d..752fcbb 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 @@
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 @@
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/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index 83f2ebe..a8713a2 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -119,7 +119,6 @@
sp<RpcServer> server = RpcServer::make(makeTransportCtxFactory(&provider));
server->setRootObject(sp<SomeBinder>::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK_EQ(OK, server->setupUnixDomainServer(kSock.c_str()));
std::thread serverThread([=] { (void)server->join(); });
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index 20c5569..e77c55c 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -44,7 +44,6 @@
auto thread = std::thread([&]() {
prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
server->setRootObject(sp<BBinder>::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
server->join();
});
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 8c359c7..19a29c1 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -208,7 +208,6 @@
],
shared_libs: [
- "android.frameworks.bufferhub@1.0",
"libbinder",
"libbufferhub",
"libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
@@ -234,7 +233,6 @@
"BufferHubProducer.cpp",
],
exclude_shared_libs: [
- "android.frameworks.bufferhub@1.0",
"libbufferhub",
"libbufferhubqueue",
"libpdx_default_transport",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 1ae90f3..9080822 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -296,7 +296,7 @@
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 @@
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 "
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 6f1a7ae..c986b82 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -130,6 +130,19 @@
return 1; // keep the callback
}
+void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const {
+ for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+ DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline =
+ event.vsync.frameTimelines[i];
+ outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId,
+ .deadlineTimestamp =
+ receiverTimeline.deadlineTimestamp,
+ .expectedPresentTime =
+ receiverTimeline.expectedVSyncTimestamp};
+ }
+}
+
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
PhysicalDisplayId* outDisplayId,
uint32_t* outCount,
@@ -154,6 +167,9 @@
outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
outVsyncEventData->frameInterval = ev.vsync.frameInterval;
outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp;
+ outVsyncEventData->preferredFrameTimelineIndex =
+ ev.vsync.preferredFrameTimelineIndex;
+ populateFrameTimelines(ev, outVsyncEventData);
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 30d19e3..b3647d6 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -301,7 +301,7 @@
// 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 3c8289f..0295099 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1150,41 +1150,6 @@
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 @@
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/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f3bd139..92c89b8 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -17,6 +17,7 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
#include <utils/Looper.h>
+#include <array>
namespace android {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -36,6 +37,26 @@
// The anticipated Vsync present time.
int64_t expectedPresentTime = 0;
+
+ struct FrameTimeline {
+ // The Vsync Id corresponsing to this vsync event. This will be used to
+ // populate ISurfaceComposer::setFrameTimelineVsync and
+ // SurfaceComposerClient::setFrameTimelineVsync
+ int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+ // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+ // frame by (both on the CPU and the GPU)
+ int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+ // The anticipated Vsync present time.
+ int64_t expectedPresentTime = 0;
+ };
+
+ // Sorted possible frame timelines.
+ std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines;
+
+ // Index into the frameTimelines that represents the platform's preferred frame timeline.
+ size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max();
};
class DisplayEventDispatcher : public LooperCallback {
@@ -77,5 +98,8 @@
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
+
+ void populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0dffbde..ca36843 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -49,6 +49,9 @@
// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
+ // Max amount of frame timelines is arbitrarily set to be reasonable.
+ static constexpr int64_t kFrameTimelinesLength = 7;
+
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -77,6 +80,12 @@
nsecs_t deadlineTimestamp __attribute__((aligned(8)));
nsecs_t frameInterval __attribute__((aligned(8)));
int64_t vsyncId;
+ size_t preferredFrameTimelineIndex __attribute__((aligned(8)));
+ struct FrameTimeline {
+ nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+ nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+ int64_t vsyncId;
+ } frameTimelines[kFrameTimelinesLength];
};
struct Hotplug {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 408497d..e0183ad 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -512,14 +512,6 @@
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 @@
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/Surface.h b/libs/gui/include/gui/Surface.h
index e540351..40d096e 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -429,11 +429,11 @@
uint32_t mReqHeight;
// mReqFormat is the buffer pixel format that will be requested at the next
- // deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
+ // dequeue operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
PixelFormat mReqFormat;
// mReqUsage is the set of buffer usage flags that will be requested
- // at the next deuque operation. It is initialized to 0.
+ // at the next dequeue operation. It is initialized to 0.
uint64_t mReqUsage;
// mTimestamp is the timestamp that will be used for the next buffer queue
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a9f4d09..b2baea6 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -885,10 +885,6 @@
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 390ff96..24a7720 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -56,13 +56,8 @@
transformedPoint.y -= origin.y;
// Derive the transformed vector's clockwise angle from vertical.
- float result = atan2f(transformedPoint.x, -transformedPoint.y);
- if (result < -M_PI_2) {
- result += M_PI;
- } else if (result > M_PI_2) {
- result -= M_PI;
- }
- return result;
+ // The return value of atan2f is in range [-pi, pi] which conforms to the orientation API.
+ return atan2f(transformedPoint.x, -transformedPoint.y);
}
vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
@@ -71,11 +66,21 @@
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
@@ -498,44 +503,14 @@
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
- if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- const vec2 xy = calculateTransformedXY(mSource, mRawTransform, coords->getXYValue());
- static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
- return xy[axis];
- }
-
- if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
- const vec2 relativeXy =
- transformWithoutTranslation(mRawTransform,
- {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
- coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
- return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
- }
-
- return coords->getAxisValue(axis);
+ const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+ return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords);
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
- if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- const vec2 xy = calculateTransformedXY(mSource, mTransform, coords->getXYValue());
- static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
- return xy[axis];
- }
-
- if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
- const vec2 relativeXy =
- transformWithoutTranslation(mTransform,
- {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
- coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
- return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
- }
-
- return coords->getAxisValue(axis);
+ const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+ return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -574,15 +549,6 @@
ui::Transform newTransform;
newTransform.set(matrix);
mTransform = newTransform * mTransform;
-
- // We need to update the AXIS_ORIENTATION value here to maintain the old behavior where the
- // orientation angle is not affected by the initial transformation set in the MotionEvent.
- std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
- [&newTransform](PointerCoords& c) {
- float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
- c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(newTransform, orientation));
- });
}
void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
@@ -751,7 +717,7 @@
#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:
@@ -808,10 +774,48 @@
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 = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
+ static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+ return xy[axis];
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
+ const vec2 relativeXy =
+ transformWithoutTranslation(transform,
+ {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
+ return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_ORIENTATION) {
+ return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ }
+
+ return coords.getAxisValue(axis);
}
// --- FocusEvent ---
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 69ae9a0..015bd81 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -89,8 +89,15 @@
// 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/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index a44f0b7..a6465ee 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -18,10 +18,10 @@
//#define LOG_NDEBUG 0
// Log debug messages about velocity tracking.
-#define DEBUG_VELOCITY 0
+static constexpr bool DEBUG_VELOCITY = false;
// Log debug messages about the progress of the algorithm itself.
-#define DEBUG_STRATEGY 0
+static constexpr bool DEBUG_STRATEGY = false;
#include <array>
#include <inttypes.h>
@@ -30,7 +30,6 @@
#include <optional>
#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
#include <input/VelocityTracker.h>
#include <utils/BitSet.h>
#include <utils/Timers.h>
@@ -64,7 +63,6 @@
return sqrtf(r);
}
-#if DEBUG_STRATEGY || DEBUG_VELOCITY
static std::string vectorToString(const float* a, uint32_t m) {
std::string str;
str += "[";
@@ -77,9 +75,11 @@
str += " ]";
return str;
}
-#endif
-#if DEBUG_STRATEGY
+static std::string vectorToString(const std::vector<float>& v) {
+ return vectorToString(v.data(), v.size());
+}
+
static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
std::string str;
str = "[";
@@ -99,7 +99,6 @@
str += " ]";
return str;
}
-#endif
// --- VelocityTracker ---
@@ -133,12 +132,18 @@
VelocityTracker::Strategy strategy) {
switch (strategy) {
case VelocityTracker::Strategy::IMPULSE:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing impulse strategy");
+ }
return std::make_unique<ImpulseVelocityTrackerStrategy>();
case VelocityTracker::Strategy::LSQ1:
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
case VelocityTracker::Strategy::LSQ2:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing lsq2 strategy");
+ }
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
case VelocityTracker::Strategy::LSQ3:
@@ -204,10 +209,10 @@
if ((mCurrentPointerIdBits.value & idBits.value)
&& eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
- (eventTime - mLastEventTime) * 0.000001f);
-#endif
+ if (DEBUG_VELOCITY) {
+ ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
+ (eventTime - mLastEventTime) * 0.000001f);
+ }
// We have not received any movements for too long. Assume that all pointers
// have stopped.
mStrategy->clear();
@@ -221,24 +226,24 @@
mStrategy->addMovement(eventTime, idBits, positions);
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d",
- eventTime, idBits.value, mActivePointerId);
- for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
- uint32_t id = iterBits.firstMarkedBit();
- uint32_t index = idBits.getIndexOfBit(id);
- iterBits.clearBit(id);
- Estimator estimator;
- getEstimator(id, &estimator);
- ALOGD(" %d: position (%0.3f, %0.3f), "
- "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
- id, positions[index].x, positions[index].y,
- int(estimator.degree),
- vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
- vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
- estimator.confidence);
+ if (DEBUG_VELOCITY) {
+ ALOGD("VelocityTracker: addMovement eventTime=%" PRId64
+ ", idBits=0x%08x, activePointerId=%d",
+ eventTime, idBits.value, mActivePointerId);
+ for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
+ uint32_t id = iterBits.firstMarkedBit();
+ uint32_t index = idBits.getIndexOfBit(id);
+ iterBits.clearBit(id);
+ Estimator estimator;
+ getEstimator(id, &estimator);
+ ALOGD(" %d: position (%0.3f, %0.3f), "
+ "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
+ id, positions[index].x, positions[index].y, int(estimator.degree),
+ vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
+ vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
+ estimator.confidence);
+ }
}
-#endif
}
void VelocityTracker::addMovement(const MotionEvent* event) {
@@ -419,11 +424,10 @@
static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
const size_t m = x.size();
-#if DEBUG_STRATEGY
- ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
- vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
- vectorToString(w, m).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
+ vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str());
+ }
LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
// Expand the X vector to a matrix A, pre-multiplied by the weights.
@@ -434,9 +438,9 @@
a[i][h] = a[i - 1][h] * x[h];
}
}
-#if DEBUG_STRATEGY
- ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
+ }
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
float q[n][m]; // orthonormal basis, column-major order
@@ -455,9 +459,9 @@
float norm = vectorNorm(&q[j][0], m);
if (norm < 0.000001f) {
// vectors are linearly dependent or zero so no solution
-#if DEBUG_STRATEGY
- ALOGD(" - no solution, norm=%f", norm);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - no solution, norm=%f", norm);
+ }
return false;
}
@@ -469,22 +473,22 @@
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
}
}
-#if DEBUG_STRATEGY
- ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
- ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
+ ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
- // calculate QR, if we factored A correctly then QR should equal A
- float qr[n][m];
- for (uint32_t h = 0; h < m; h++) {
- for (uint32_t i = 0; i < n; i++) {
- qr[i][h] = 0;
- for (uint32_t j = 0; j < n; j++) {
- qr[i][h] += q[j][h] * r[j][i];
+ // calculate QR, if we factored A correctly then QR should equal A
+ float qr[n][m];
+ for (uint32_t h = 0; h < m; h++) {
+ for (uint32_t i = 0; i < n; i++) {
+ qr[i][h] = 0;
+ for (uint32_t j = 0; j < n; j++) {
+ qr[i][h] += q[j][h] * r[j][i];
+ }
}
}
+ ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
}
- ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
// We just work from bottom-right to top-left calculating B's coefficients.
@@ -500,9 +504,9 @@
}
outB[i] /= r[i][i];
}
-#if DEBUG_STRATEGY
- ALOGD(" - b=%s", vectorToString(outB, n).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - b=%s", vectorToString(outB, n).c_str());
+ }
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
// SSerr is the residual sum of squares (variance of the error),
@@ -528,11 +532,11 @@
sstot += w[h] * w[h] * var * var;
}
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
-#if DEBUG_STRATEGY
- ALOGD(" - sserr=%f", sserr);
- ALOGD(" - sstot=%f", sstot);
- ALOGD(" - det=%f", *outDet);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - sserr=%f", sserr);
+ ALOGD(" - sstot=%f", sstot);
+ ALOGD(" - det=%f", *outDet);
+ }
return true;
}
@@ -655,13 +659,11 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
-#if DEBUG_STRATEGY
- ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
- int(outEstimator->degree),
- vectorToString(outEstimator->xCoeff, n).c_str(),
- vectorToString(outEstimator->yCoeff, n).c_str(),
- outEstimator->confidence);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+ int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
+ vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
+ }
return true;
}
}
@@ -1169,9 +1171,9 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = 2; // similar results to 2nd degree fit
outEstimator->confidence = 1;
-#if DEBUG_STRATEGY
- ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+ }
return true;
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index caf3a61..a92016b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -444,12 +444,19 @@
ASSERT_EQ(217, event->getToolMinor(0));
ASSERT_EQ(227, event->getToolMinor(1));
- ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
- ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
- ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
- ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
- ASSERT_EQ(218, event->getOrientation(0));
- ASSERT_EQ(228, event->getOrientation(1));
+ // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is "up",
+ // and the positive y direction is "down".
+ auto toScaledOrientation = [](float angle) {
+ const float x = sinf(angle) * X_SCALE;
+ const float y = -cosf(angle) * Y_SCALE;
+ return atan2f(x, -y);
+ };
+ ASSERT_EQ(toScaledOrientation(18), event->getHistoricalOrientation(0, 0));
+ ASSERT_EQ(toScaledOrientation(28), event->getHistoricalOrientation(1, 0));
+ ASSERT_EQ(toScaledOrientation(118), event->getHistoricalOrientation(0, 1));
+ ASSERT_EQ(toScaledOrientation(128), event->getHistoricalOrientation(1, 1));
+ ASSERT_EQ(toScaledOrientation(218), event->getOrientation(0));
+ ASSERT_EQ(toScaledOrientation(228), event->getOrientation(1));
}
TEST_F(MotionEventTest, Properties) {
@@ -518,6 +525,7 @@
TEST_F(MotionEventTest, Scale) {
MotionEvent event;
initializeEventWithHistory(&event);
+ const float unscaledOrientation = event.getOrientation(0);
event.scale(2.0f);
@@ -534,7 +542,7 @@
ASSERT_EQ(215 * 2, event.getTouchMinor(0));
ASSERT_EQ(216 * 2, event.getToolMajor(0));
ASSERT_EQ(217 * 2, event.getToolMinor(0));
- ASSERT_EQ(218, event.getOrientation(0));
+ ASSERT_EQ(unscaledOrientation, event.getOrientation(0));
}
TEST_F(MotionEventTest, Parcel) {
@@ -639,9 +647,8 @@
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;
@@ -652,8 +659,8 @@
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,
@@ -662,6 +669,13 @@
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;
@@ -700,16 +714,39 @@
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 d09f2ac..973194c 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -259,7 +259,13 @@
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i));
- EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(i));
+
+ // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is
+ // "up", and the positive y direction is "down".
+ const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ const float x = sinf(unscaledOrientation) * xScale;
+ const float y = -cosf(unscaledOrientation) * yScale;
+ EXPECT_EQ(atan2f(x, -y), motionEvent->getOrientation(i));
}
status = mConsumer->sendFinishedSignal(seq, false);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 3039362..a87b187 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -343,7 +343,7 @@
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
- 872.794617);
+ 764.345703);
computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
951.698181);
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 79d9b93..fc9680b 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -100,17 +100,10 @@
* Implementation of AChoreographerFrameCallbackData.
*/
struct ChoreographerFrameCallbackDataImpl {
- struct FrameTimeline {
- int64_t vsyncId{0};
- int64_t expectedPresentTimeNanos{0};
- int64_t deadlineNanos{0};
- };
-
int64_t frameTimeNanos{0};
- size_t frameTimelinesLength;
-
- std::vector<FrameTimeline> frameTimelines;
+ std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength>
+ frameTimelines;
size_t preferredFrameTimelineIndex;
@@ -456,14 +449,9 @@
}
ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
- std::vector<ChoreographerFrameCallbackDataImpl::FrameTimeline> frameTimelines;
- frameTimelines.push_back({.vsyncId = mLastVsyncEventData.id,
- .expectedPresentTimeNanos = mLastVsyncEventData.expectedPresentTime,
- .deadlineNanos = mLastVsyncEventData.deadlineTimestamp});
return {.frameTimeNanos = timestamp,
- .frameTimelinesLength = 1,
- .preferredFrameTimelineIndex = 0,
- .frameTimelines = frameTimelines,
+ .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex,
+ .frameTimelines = mLastVsyncEventData.frameTimelines,
.choreographer = this};
}
@@ -646,7 +634,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- return frameCallbackData->frameTimelinesLength;
+ return frameCallbackData->frameTimelines.size();
}
size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
const AChoreographerFrameCallbackData* data) {
@@ -662,8 +650,8 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
- return frameCallbackData->frameTimelines[index].vsyncId;
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].id;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
const AChoreographerFrameCallbackData* data, size_t index) {
@@ -671,8 +659,8 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
- return frameCallbackData->frameTimelines[index].expectedPresentTimeNanos;
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].expectedPresentTime;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
const AChoreographerFrameCallbackData* data, size_t index) {
@@ -680,8 +668,8 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
- return frameCallbackData->frameTimelines[index].deadlineNanos;
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].deadlineTimestamp;
}
AChoreographer* AChoreographer_create() {
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 2f31888..6882ea3 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -191,7 +191,7 @@
// 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/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index a299488..0565f42 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -15,6 +15,13 @@
*/
/**
+ * @defgroup ADataSpace Data Space
+ *
+ * ADataSpace describes how to interpret colors.
+ * @{
+ */
+
+/**
* @file data_space.h
*/
@@ -517,3 +524,5 @@
__END_DECLS
#endif // ANDROID_DATA_SPACE_H
+
+/** @} */
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 570c7bc..a62d2b9 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -94,6 +94,8 @@
"skia/debug/SkiaCapture.cpp",
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
+ "skia/filters/GaussianBlurFilter.cpp",
+ "skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
"skia/filters/StretchShaderFactory.cpp"
],
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index d5ec774..21d5603 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -53,6 +53,8 @@
#include "SkBlendMode.h"
#include "SkImageInfo.h"
#include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
@@ -328,7 +330,7 @@
if (args.supportsBackgroundBlur) {
ALOGD("Background Blurs Enabled");
- mBlurFilter = new BlurFilter();
+ mBlurFilter = new KawaseBlurFilter();
}
mCapture = std::make_unique<SkiaCapture>();
}
@@ -803,11 +805,11 @@
continue;
}
if (layer.backgroundBlurRadius > 0 &&
- layer.backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
for (auto region : layer.blurRegions) {
- if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
}
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 2b6833e..6746e47 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -15,7 +15,6 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
#include "BlurFilter.h"
#include <SkCanvas.h>
#include <SkData.h>
@@ -32,33 +31,7 @@
namespace renderengine {
namespace skia {
-BlurFilter::BlurFilter() {
- SkString blurString(R"(
- uniform shader child;
- uniform float2 in_blurOffset;
- uniform float2 in_maxSizeXY;
-
- half4 main(float2 xy) {
- half4 c = child.eval(xy);
- c += child.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += child.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += child.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += child.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-
- return half4(c.rgb * 0.2, 1.0);
- }
- )");
-
- auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
- if (!blurEffect) {
- LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
- }
- mBlurEffect = std::move(blurEffect);
-
+static sk_sp<SkRuntimeEffect> createMixEffect() {
SkString mixString(R"(
uniform shader blurredInput;
uniform shader originalInput;
@@ -73,58 +46,12 @@
if (!mixEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
}
- mMixEffect = std::move(mixEffect);
+ return mixEffect;
}
-sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
- const sk_sp<SkImage> input, const SkRect& blurRect) const {
- // Kawase is an approximation of Gaussian, but it behaves differently from it.
- // A radius transformation is required for approximating them, and also to introduce
- // non-integer steps, necessary to smoothly interpolate large radii.
- float tmpRadius = (float)blurRadius / 2.0f;
- float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
- float radiusByPasses = tmpRadius / (float)numberOfPasses;
-
- // create blur surface with the bit depth and colorspace of the original surface
- SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
- std::ceil(blurRect.height() * kInputScale));
-
- const float stepX = radiusByPasses;
- const float stepY = radiusByPasses;
-
- // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
- // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
- // but instead we must do the inverse.
- SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
- blurMatrix.postScale(kInputScale, kInputScale);
-
- // start by downscaling and doing the first blur pass
- SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
- SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
- blurBuilder.child("child") =
- input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
-
- sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
-
- // And now we'll build our chain of scaled blur stages
- for (auto i = 1; i < numberOfPasses; i++) {
- const float stepScale = (float)i * kInputScale;
- blurBuilder.child("child") =
- tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
- tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
- }
-
- return tmpBlur;
-}
-
-static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
- // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect,
+ const float scale) {
+ // 1. Apply the blur shader matrix, which scales up the blurred surface to its real size
auto matrix = SkMatrix::Scale(scale, scale);
// 2. Since the blurred surface has the size of the layer, we align it with the
// top left corner of the layer position.
@@ -139,6 +66,14 @@
return matrix;
}
+BlurFilter::BlurFilter(const float maxCrossFadeRadius)
+ : mMaxCrossFadeRadius(maxCrossFadeRadius),
+ mMixEffect(maxCrossFadeRadius > 0 ? createMixEffect() : nullptr) {}
+
+float BlurFilter::getMaxCrossFadeRadius() const {
+ return mMaxCrossFadeRadius;
+}
+
void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
@@ -153,7 +88,7 @@
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < mMaxCrossFadeRadius) {
// For sampling Skia's API expects the inverse of what logically seems appropriate. In this
// case you might expect the matrix to simply be the canvas matrix.
SkMatrix inputMatrix;
@@ -166,7 +101,7 @@
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / mMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 7110018..9cddc75 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -27,29 +27,19 @@
namespace renderengine {
namespace skia {
-/**
- * This is an implementation of a Kawase blur, as described in here:
- * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
- * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
- */
class BlurFilter {
public:
// Downsample FBO to improve performance
static constexpr float kInputScale = 0.25f;
// Downsample scale factor used to improve performance
static constexpr float kInverseInputScale = 1.0f / kInputScale;
- // Maximum number of render passes
- static constexpr uint32_t kMaxPasses = 4;
- // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
- // image, up to this radius.
- static constexpr float kMaxCrossFadeRadius = 10.0f;
- explicit BlurFilter();
- virtual ~BlurFilter(){};
+ explicit BlurFilter(float maxCrossFadeRadius = 10.0f);
+ virtual ~BlurFilter(){}
// Execute blur, saving it to a texture
- sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
- const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+ virtual sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const = 0;
/**
* Draw the blurred content (from the generate method) into the canvas.
@@ -61,13 +51,20 @@
* @param blurredImage down-sampled blurred content that was produced by the generate() method
* @param input original unblurred input that is used to crossfade with the blurredImage
*/
- void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
- const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
- sk_sp<SkImage> input);
+ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
+ const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input);
+
+ float getMaxCrossFadeRadius() const;
private:
- sk_sp<SkRuntimeEffect> mBlurEffect;
- sk_sp<SkRuntimeEffect> mMixEffect;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ const float mMaxCrossFadeRadius;
+
+ // Optional blend used for crossfade only if mMaxCrossFadeRadius > 0
+ const sk_sp<SkRuntimeEffect> mMixEffect;
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
new file mode 100644
index 0000000..55867a9
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GaussianBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkImageFilters.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// This constant approximates the scaling done in the software path's
+// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+static const float BLUR_SIGMA_SCALE = 0.57735f;
+
+GaussianBlurFilter::GaussianBlurFilter(): BlurFilter(/* maxCrossFadeRadius= */ 0.0f) {}
+
+sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // Create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+ sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, scaledInfo);
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ paint.setImageFilter(SkImageFilters::Blur(
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ SkTileMode::kClamp, nullptr));
+
+ surface->getCanvas()->drawImageRect(
+ input,
+ blurRect,
+ SkRect::MakeWH(scaledInfo.width(), scaledInfo.height()),
+ SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone},
+ &paint,
+ SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ return surface->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.h b/libs/renderengine/skia/filters/GaussianBlurFilter.h
new file mode 100644
index 0000000..a4febd2
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.h
@@ -0,0 +1,47 @@
+/*
+ * 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 "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Gaussian blur using Skia's built-in GaussianBlur filter.
+ */
+class GaussianBlurFilter: public BlurFilter {
+public:
+ explicit GaussianBlurFilter();
+ virtual ~GaussianBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
new file mode 100644
index 0000000..bfde06f
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
+ SkString blurString(R"(
+ uniform shader child;
+ uniform float in_blurOffset;
+
+ half4 main(float2 xy) {
+ half4 c = child.eval(xy);
+ c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
+ c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
+ return half4(c.rgb * 0.2, 1.0);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ if (!blurEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+ }
+ mBlurEffect = std::move(blurEffect);
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ float tmpRadius = (float)blurRadius / 2.0f;
+ float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+ // create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+
+ // start by downscaling and doing the first blur pass
+ SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+ blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
+
+ sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+
+ // And now we'll build our chain of scaled blur stages
+ for (auto i = 1; i < numberOfPasses; i++) {
+ blurBuilder.child("child") =
+ tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+ blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
+ tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+ }
+
+ return tmpBlur;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.h b/libs/renderengine/skia/filters/KawaseBlurFilter.h
new file mode 100644
index 0000000..0ac5ac8
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.h
@@ -0,0 +1,54 @@
+/*
+ * 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 "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class KawaseBlurFilter: public BlurFilter {
+public:
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+
+ explicit KawaseBlurFilter();
+ virtual ~KawaseBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 7634007..9aae145 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -124,7 +124,7 @@
// 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 162ec02..71adee4 100644
--- a/libs/ui/include_mock/ui/MockFence.h
+++ b/libs/ui/include_mock/ui/MockFence.h
@@ -27,6 +27,7 @@
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 6e520b1..40dddc3 100644
--- a/libs/ui/tests/MockFence_test.cpp
+++ b/libs/ui/tests/MockFence_test.cpp
@@ -42,4 +42,16 @@
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/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
index 2836467..2c5fe21 100644
--- a/services/inputflinger/dispatcher/EventLogTags.logtags
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -37,6 +37,7 @@
62000 input_interaction (windows|4)
62001 input_focus (window|3),(reason|3)
+62003 input_cancel (window|3),(reason|3)
# NOTE - the range 1000000-2000000 is reserved for partners and others who
# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ea5799a..1b19311 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -153,6 +153,7 @@
// Event log tags. See EventLogTags.logtags for reference
constexpr int LOGTAG_INPUT_INTERACTION = 62000;
constexpr int LOGTAG_INPUT_FOCUS = 62001;
+constexpr int LOGTAG_INPUT_CANCEL = 62003;
inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -341,18 +342,6 @@
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,
@@ -2077,6 +2066,8 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
+ ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
+ displayId);
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
@@ -2870,6 +2861,11 @@
if (!splitMotionEntry) {
return; // split event was dropped
}
+ if (splitMotionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+ std::string reason = std::string("reason=pointer cancel on split window");
+ android_log_event_list(LOGTAG_INPUT_CANCEL)
+ << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
+ }
if (DEBUG_FOCUS) {
ALOGD("channel '%s' ~ Split motion event.",
connection->getInputChannelName().c_str());
@@ -3575,6 +3571,10 @@
options.mode);
}
+ std::string reason = std::string("reason=").append(options.reason);
+ android_log_event_list(LOGTAG_INPUT_CANCEL)
+ << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
+
InputTarget target;
sp<WindowInfoHandle> windowHandle =
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 1879cec..d8fd16c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2841,6 +2841,17 @@
ASSERT_EQ(ui::Transform(), event->getTransform());
}
+TEST_F(InputDispatcherTest, GestureMonitor_NoWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -3001,59 +3012,6 @@
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/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 969fd67..861d496 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -227,7 +227,7 @@
* of a camera where the buffer remains in native orientation,
* we want the pixels to always be upright.
*/
- auto p = mDrawingParent;
+ sp<Layer> p = mDrawingParent.promote();
if (p != nullptr) {
const auto parentTransform = p->getTransform();
tr = tr * inverseOrientation(parentTransform.getOrientation());
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 8c4c8b7..a4c21f4 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -171,7 +171,7 @@
// 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 @@
// 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 4e5d2d0..28c387e 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -118,7 +118,7 @@
bool BufferQueueLayer::fenceHasSignaled() const {
Mutex::Autolock lock(mQueueItemLock);
- if (SurfaceFlinger::enableLatchUnsignaled) {
+ if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
}
@@ -216,7 +216,7 @@
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 @@
// 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();
}
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index c213570..b6cbbb6 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -549,13 +549,19 @@
}
bool BufferStateLayer::setTransactionCompletedListeners(
- const std::vector<sp<CallbackHandle>>& handles) {
+ const std::vector<ListenerCallbacks>& listenerCallbacks, const sp<IBinder>& layerHandle) {
// If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
- if (handles.empty()) {
+ if (listenerCallbacks.empty()) {
mReleasePreviousBuffer = false;
return false;
}
+ std::vector<sp<CallbackHandle>> handles;
+ handles.reserve(listenerCallbacks.size());
+ for (auto& [listener, callbackIds] : listenerCallbacks) {
+ handles.emplace_back(new CallbackHandle(listener, callbackIds, layerHandle));
+ }
+
const bool willPresent = willPresentCurrentTransaction();
for (const auto& handle : handles) {
@@ -571,9 +577,10 @@
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
- } else { // If this layer will NOT need to be relatched and presented this frame
+ } else {
+ // If this layer will NOT need to be relatched and presented this frame
// Notify the transaction completed thread this handle is done
- mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+ mFlinger->getTransactionCallbackInvoker().addUnpresentedCallbackHandle(handle);
}
}
@@ -630,7 +637,7 @@
// Interface implementation for BufferLayer
// -----------------------------------------------------------------------
bool BufferStateLayer::fenceHasSignaled() const {
- if (SurfaceFlinger::enableLatchUnsignaled) {
+ if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
}
@@ -660,9 +667,7 @@
}
void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
- if (!mAutoRefresh.exchange(autoRefresh)) {
- mFlinger->onLayerUpdate();
- }
+ mDrawingState.autoRefresh = autoRefresh;
}
bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
@@ -937,7 +942,7 @@
* how to go from screen space back to window space.
*/
ui::Transform BufferStateLayer::getInputTransform() const {
- auto parent = mDrawingParent;
+ sp<Layer> parent = mDrawingParent.promote();
if (parent == nullptr) {
return ui::Transform();
}
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index eea700c..ceed188 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -65,7 +65,8 @@
bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
bool setApi(int32_t api) override;
bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
- bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+ bool setTransactionCompletedListeners(const std::vector<ListenerCallbacks>& handles,
+ const sp<IBinder>& layerHandle) override;
bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime) override;
bool setPosition(float /*x*/, float /*y*/) override;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index e26ab11..82a9ae2 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -326,7 +326,7 @@
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 @@
}
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/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index bd7b9a5..438b1aa 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 @@
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 671395f..4ada2b6 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 @@
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 3b98d50..d85e843 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -179,23 +179,9 @@
if (mDrawingState.sidebandStream != nullptr) {
mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
}
-
if (mHadClonedChild) {
mFlinger->mNumClones--;
}
-
- for (auto const& child : mCurrentChildren) {
- if (child->mCurrentParent == this) child->mCurrentParent = nullptr;
- if (child->mDrawingParent == this) {
- child->mDrawingParent = nullptr;
- }
- }
- for (auto const& child : mDrawingChildren) {
- if (child->mCurrentParent == this) child->mCurrentParent = nullptr;
- if (child->mDrawingParent == this) {
- child->mDrawingParent = nullptr;
- }
- }
}
LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
@@ -252,7 +238,7 @@
}
sp<Layer> Layer::getRootLayer() {
- auto parent = getParent();
+ sp<Layer> parent = getParent();
if (parent == nullptr) {
return this;
}
@@ -434,8 +420,6 @@
compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
compositionState->alpha = alpha;
- compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
- compositionState->blurRegions = drawingState.blurRegions;
compositionState->stretchEffect = getStretchEffect();
}
@@ -497,6 +481,9 @@
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() {
@@ -677,7 +664,7 @@
return true;
}
- const auto p = mDrawingParent;
+ const auto p = mDrawingParent.promote();
return (p != nullptr) ? p->isSecure() : false;
}
@@ -860,7 +847,7 @@
if (getDrawingState().isTrustedOverlay) {
return true;
}
- const auto p = mDrawingParent;
+ const auto& p = mDrawingParent.promote();
return (p != nullptr) && p->isTrustedOverlay();
}
@@ -937,8 +924,11 @@
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);
@@ -971,6 +961,11 @@
}
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);
@@ -1040,7 +1035,7 @@
return mDrawingState.frameRateSelectionPriority;
}
// If not, search whether its parents have it set.
- auto parent = getParent();
+ sp<Layer> parent = getParent();
if (parent != nullptr) {
return parent->getFrameRateSelectionPriority();
}
@@ -1053,11 +1048,10 @@
};
ui::LayerStack Layer::getLayerStack() const {
- auto p = mDrawingParent;
- if (p == nullptr) {
- return getDrawingState().layerStack;
+ if (const auto parent = mDrawingParent.promote()) {
+ return parent->getLayerStack();
}
- return mDrawingParent->getLayerStack();
+ return getDrawingState().layerStack;
}
bool Layer::setShadowRadius(float shadowRadius) {
@@ -1102,7 +1096,7 @@
return mDrawingState.stretchEffect;
}
- auto parent = mDrawingParent;
+ sp<Layer> parent = getParent();
if (parent != nullptr) {
auto effect = parent->getStretchEffect();
if (effect.hasEffect()) {
@@ -1172,9 +1166,6 @@
}
bool Layer::setFrameRate(FrameRate frameRate) {
- if (!mFlinger->useFrameRateApi) {
- return false;
- }
if (mDrawingState.frameRate == frameRate) {
return false;
}
@@ -1317,7 +1308,7 @@
bool Layer::isHiddenByPolicy() const {
const State& s(mDrawingState);
- auto parent = mDrawingParent;
+ const auto& parent = mDrawingParent.promote();
if (parent != nullptr && parent->isHiddenByPolicy()) {
return true;
}
@@ -1364,7 +1355,7 @@
LayerDebugInfo info;
const State& ds = getDrawingState();
info.mName = getName();
- auto parent = mDrawingParent;
+ sp<Layer> parent = mDrawingParent.promote();
info.mParentName = parent ? parent->getName() : "none"s;
info.mType = getType();
info.mTransparentRegion = ds.activeTransparentRegion_legacy;
@@ -1596,7 +1587,7 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
- child->mDrawingParent = newParent.get();
+ child->mDrawingParent = newParent;
child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
newParent->mEffectiveShadowRadius);
}
@@ -1616,7 +1607,7 @@
}
}
- auto parent = getParent();
+ sp<Layer> parent = getParent();
if (parent != nullptr) {
parent->removeChild(this);
}
@@ -1651,7 +1642,7 @@
mat4 Layer::getColorTransform() const {
mat4 colorTransform = mat4(getDrawingState().colorTransform);
- if (auto parent = mDrawingParent; parent != nullptr) {
+ if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
colorTransform = parent->getColorTransform() * colorTransform;
}
return colorTransform;
@@ -1659,7 +1650,7 @@
bool Layer::hasColorTransform() const {
bool hasColorTransform = getDrawingState().hasColorTransform;
- if (auto parent = mDrawingParent; parent != nullptr) {
+ if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
hasColorTransform = hasColorTransform || parent->hasColorTransform();
}
return hasColorTransform;
@@ -1673,7 +1664,7 @@
}
void Layer::setParent(const sp<Layer>& layer) {
- mCurrentParent = layer.get();
+ mCurrentParent = layer;
}
int32_t Layer::getZ(LayerVector::StateSet) const {
@@ -1877,7 +1868,7 @@
}
half Layer::getAlpha() const {
- auto p = mDrawingParent;
+ const auto& p = mDrawingParent.promote();
half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
return parentAlpha * getDrawingState().color.a;
@@ -1888,7 +1879,7 @@
if (fixedTransformHint != ui::Transform::ROT_INVALID) {
return fixedTransformHint;
}
- auto p = mCurrentParent;
+ const auto& p = mCurrentParent.promote();
if (!p) return fixedTransformHint;
return p->getFixedTransformHint();
}
@@ -1899,7 +1890,7 @@
}
int32_t Layer::getBackgroundBlurRadius() const {
- auto p = mDrawingParent;
+ const auto& p = mDrawingParent.promote();
half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
return parentAlpha * getDrawingState().backgroundBlurRadius;
@@ -1917,8 +1908,9 @@
Layer::RoundedCornerState Layer::getRoundedCornerState() const {
// Get parent settings
RoundedCornerState parentSettings;
- if (mDrawingParent != nullptr) {
- parentSettings = mDrawingParent->getRoundedCornerState();
+ const auto& parent = mDrawingParent.promote();
+ if (parent != nullptr) {
+ parentSettings = parent->getRoundedCornerState();
if (parentSettings.radius > 0) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
@@ -2134,7 +2126,7 @@
LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
layerInfo->mutable_requested_transform());
- auto parent = useDrawing ? mDrawingParent : mCurrentParent;
+ auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
if (parent != nullptr) {
layerInfo->set_parent(parent->sequence);
} else {
@@ -2281,9 +2273,9 @@
}
void Layer::fillTouchOcclusionMode(WindowInfo& info) {
- Layer* p = this;
+ sp<Layer> p = this;
while (p != nullptr && !p->hasInputInfo()) {
- p = p->mDrawingParent;
+ p = p->mDrawingParent.promote();
}
if (p != nullptr) {
info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode;
@@ -2295,8 +2287,9 @@
if (mode == gui::DropInputMode::ALL) {
return mode;
}
- if (mDrawingParent) {
- gui::DropInputMode parentMode = mDrawingParent->getDropInputMode();
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent) {
+ gui::DropInputMode parentMode = parent->getDropInputMode();
if (parentMode != gui::DropInputMode::NONE) {
return parentMode;
}
@@ -2323,7 +2316,8 @@
}
// Check if the parent has set an alpha on the layer
- if (mDrawingParent && mDrawingParent->getAlpha() != 1.0_hf) {
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent && parent->getAlpha() != 1.0_hf) {
info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
static_cast<float>(getAlpha()));
@@ -2421,10 +2415,10 @@
if (mClonedChild != nullptr) {
return this;
}
- if (mDrawingParent == nullptr) {
+ if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
return nullptr;
}
- return mDrawingParent->getClonedRoot();
+ return mDrawingParent.promote()->getClonedRoot();
}
bool Layer::hasInputInfo() const {
@@ -2611,7 +2605,8 @@
return true;
}
- return mDrawingParent && mDrawingParent->isInternalDisplayOverlay();
+ sp<Layer> parent = mDrawingParent.promote();
+ return parent && parent->isInternalDisplayOverlay();
}
void Layer::setClonedChild(const sp<Layer>& clonedChild) {
@@ -2645,6 +2640,17 @@
return true;
}
+bool Layer::setTransactionCompletedListeners(
+ const std::vector<ListenerCallbacks>& listenerCallbacks, const sp<IBinder>&) {
+ if (listenerCallbacks.empty()) {
+ return false;
+ }
+ for (auto& listener : listenerCallbacks) {
+ mFlinger->getTransactionCallbackInvoker().addEmptyCallback(listener);
+ }
+ return false;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4569f9a..b79903d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -280,6 +280,8 @@
sp<IBinder> releaseBufferEndpoint;
gui::DropInputMode dropInputMode;
+
+ bool autoRefresh = false;
};
/*
@@ -426,10 +428,8 @@
virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
virtual bool setApi(int32_t /*api*/) { return false; };
virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
- virtual bool setTransactionCompletedListeners(
- const std::vector<sp<CallbackHandle>>& /*handles*/) {
- return false;
- };
+ virtual bool setTransactionCompletedListeners(const std::vector<ListenerCallbacks>& /*handles*/,
+ const sp<IBinder>& /* layerHandle */);
virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
nsecs_t /*requestedPresentTime*/) {
return false;
@@ -796,12 +796,12 @@
// Returns index if removed, or negative value otherwise
// for symmetry with Vector::remove
ssize_t removeChild(const sp<Layer>& layer);
+ sp<Layer> getParent() const { return mCurrentParent.promote(); }
// Should be called with the surfaceflinger statelock held
bool isAtRoot() const { return mIsAtRoot; }
void setIsAtRoot(bool isAtRoot) { mIsAtRoot = isAtRoot; }
- Layer* getParent() const { return mCurrentParent; }
bool hasParent() const { return getParent() != nullptr; }
Rect getScreenBounds(bool reduceTransparentRegion = true) const;
bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
@@ -1008,8 +1008,8 @@
LayerVector mCurrentChildren{LayerVector::StateSet::Current};
LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
- Layer* mCurrentParent = nullptr;
- Layer* mDrawingParent = nullptr;
+ wp<Layer> mCurrentParent;
+ wp<Layer> mDrawingParent;
// Window types from WindowManager.LayoutParams
const gui::WindowInfo::Type mWindowType;
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index e84508f..11fe6d0 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -94,8 +94,22 @@
// no need to check rotation because there is none
mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
+ // If layer is offscreen, update mirroring info if it exists
+ if (mLayer->isRemovedFromCurrentState()) {
+ mLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->updateMirrorInfo(); });
+ mLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->updateCloneBufferInfo(); });
+ }
+
if (!mChildrenOnly) {
mTransform = mLayer->getTransform().inverse();
+ // If the layer is offscreen, compute bounds since we don't compute bounds for offscreen
+ // layers in a regular cycles.
+ if (mLayer->isRemovedFromCurrentState()) {
+ FloatRect maxBounds = mFlinger.getMaxDisplayBounds();
+ mLayer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
+ }
drawLayers();
} else {
uint32_t w = static_cast<uint32_t>(getWidth());
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 2231853..f715309 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 @@
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 2bdcaf6..455289f 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -355,14 +355,7 @@
std::lock_guard<std::mutex> lock(mMutex);
LOG_FATAL_IF(!mVSyncState);
- const int64_t vsyncId = [&] {
- if (mTokenManager != nullptr) {
- return mTokenManager->generateTokenForPredictions(
- {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
- }
- return FrameTimelineInfo::INVALID_VSYNC_ID;
- }();
-
+ const int64_t vsyncId = generateToken(timestamp, deadlineTimestamp, expectedVSyncTimestamp);
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
mCondition.notify_all();
@@ -567,12 +560,48 @@
}
}
+int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+ nsecs_t expectedVSyncTimestamp) const {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+ }
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
+}
+
+void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const {
+ // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
+ for (int multiplier = -DisplayEventReceiver::kFrameTimelinesLength + 1, currentIndex = 0;
+ currentIndex < DisplayEventReceiver::kFrameTimelinesLength; multiplier++) {
+ nsecs_t deadline = event.vsync.deadlineTimestamp + multiplier * event.vsync.frameInterval;
+ // Valid possible frame timelines must have future values.
+ if (deadline > event.header.timestamp) {
+ if (multiplier == 0) {
+ event.vsync.preferredFrameTimelineIndex = currentIndex;
+ event.vsync.frameTimelines[currentIndex] =
+ {.vsyncId = event.vsync.vsyncId,
+ .deadlineTimestamp = event.vsync.deadlineTimestamp,
+ .expectedVSyncTimestamp = event.vsync.expectedVSyncTimestamp};
+ } else {
+ nsecs_t expectedVSync =
+ event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval;
+ event.vsync.frameTimelines[currentIndex] =
+ {.vsyncId = generateToken(event.header.timestamp, deadline, expectedVSync),
+ .deadlineTimestamp = deadline,
+ .expectedVSyncTimestamp = expectedVSync};
+ }
+ currentIndex++;
+ }
+ }
+}
+
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+ generateFrameTimeline(copy);
}
switch (consumer->postEvent(copy)) {
case NO_ERROR:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 9265a25..de43570 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -204,6 +204,10 @@
void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
nsecs_t deadlineTimestamp) override;
+ 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);
frametimeline::TokenManager* const mTokenManager;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index b2b0451..2000c54 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 @@
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 e565bbb..c2dcd70 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -263,14 +263,6 @@
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 +331,8 @@
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) {
@@ -494,14 +485,22 @@
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;
@@ -1774,24 +1773,6 @@
*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;
@@ -2428,7 +2409,7 @@
}
}
-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 +2427,13 @@
// 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 */);
}
@@ -3121,7 +3107,21 @@
// 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() {
@@ -3356,8 +3356,7 @@
status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
- const sp<IBinder>& parentHandle,
- const sp<Layer>& parentLayer, bool addToRoot,
+ const wp<Layer>& parent, bool addToRoot,
uint32_t* outTransformHint) {
if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
@@ -3369,7 +3368,7 @@
if (gbc != nullptr) {
initialProducer = IInterface::asBinder(gbc);
}
- setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer, addToRoot);
+ setLayerCreatedState(handle, lbc, parent, initialProducer, addToRoot);
// Create a transaction includes the initial parent and producer.
Vector<ComposerState> states;
@@ -3381,7 +3380,6 @@
states.add(composerState);
lbc->updateTransformHint(mActiveDisplayTransformHint);
-
if (outTransformHint) {
*outTransformHint = mActiveDisplayTransformHint;
}
@@ -3422,29 +3420,34 @@
}
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;
}
@@ -3453,6 +3456,14 @@
});
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()) {
@@ -3464,52 +3475,115 @@
}
// 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());
}
- }
- // 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));
- }
+ return applyTransactions(transactions);
}
- } // unlock mStateLock
+ }
+}
+
+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));
+ }
+ }
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;
+ return (state.flags & ~layer_state_t::eBufferChanged) == 0 &&
+ state.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ state.bufferData.acquireFence &&
+ state.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled;
+ }
+ return false;
+}
+
bool SurfaceFlinger::transactionFlushNeeded() {
Mutex::Autolock _l(mQueueLock);
return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
@@ -3541,7 +3615,8 @@
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
@@ -3568,7 +3643,7 @@
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;
@@ -3629,7 +3704,7 @@
: CountDownLatch::eSyncTransaction));
}
- mTransactionQueue.emplace(state);
+ mTransactionQueue.emplace_back(state);
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
const auto schedule = [](uint32_t flags) {
@@ -3728,11 +3803,10 @@
transactionFlags |= setDisplayStateLocked(display);
}
- // start and end registration for listeners w/ no surface so they can get their callback. Note
- // that listeners with SurfaceControls will start registration during setClientStateLocked
- // below.
+ // Add listeners w/ surfaces so they can get their callback. Note that listeners with
+ // SurfaceControls will start registration during setClientStateLocked below.
for (const auto& listener : listenerCallbacks) {
- mTransactionCallbackInvoker.addEmptyTransaction(listener);
+ mTransactionCallbackInvoker.addEmptyCallback(listener);
}
uint32_t clientStateFlags = 0;
@@ -3904,7 +3978,7 @@
}
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
+ mTransactionCallbackInvoker.addUnpresentedCallbackHandle(
new CallbackHandle(listener, callbackIds, s.surface));
}
return 0;
@@ -3922,7 +3996,7 @@
}
if (what & layer_state_t::eLayerChanged) {
// NOTE: index needs to be calculated before we update the state
- auto p = layer->getParent();
+ const auto& p = layer->getParent();
if (p == nullptr) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
if (layer->setLayer(s.z) && idx >= 0) {
@@ -3940,7 +4014,7 @@
}
if (what & layer_state_t::eRelativeLayerChanged) {
// NOTE: index needs to be calculated before we update the state
- auto p = layer->getParent();
+ const auto& p = layer->getParent();
const auto& relativeHandle = s.relativeLayerSurfaceControl ?
s.relativeLayerSurfaceControl->getHandle() : nullptr;
if (p == nullptr) {
@@ -4175,12 +4249,6 @@
flags |= eTransactionNeeded | eTraversalNeeded;
}
}
- std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
- for (auto& [listener, callbackIds] : filteredListeners) {
- callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
- }
- }
if (what & layer_state_t::eBufferChanged &&
layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
@@ -4190,7 +4258,11 @@
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
- if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+ if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+ if (layer->setTransactionCompletedListeners(filteredListeners, s.surface)) {
+ flags |= eTraversalNeeded;
+ }
+ }
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
return flags;
@@ -4228,7 +4300,7 @@
}
*outLayerId = mirrorLayer->sequence;
- return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
+ return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, false,
nullptr /* outTransformHint */);
}
@@ -4297,8 +4369,15 @@
}
bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess();
- result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer, addToRoot,
- outTransformHint);
+ wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
+ if (parentHandle != nullptr && parent == nullptr) {
+ ALOGE("Invalid parent handle %p.", parentHandle.get());
+ addToRoot = false;
+ }
+ if (parentLayer != nullptr) {
+ addToRoot = false;
+ }
+ result = addClientLayer(client, *handle, *gbp, layer, parent, addToRoot, outTransformHint);
if (result != NO_ERROR) {
return result;
}
@@ -5155,11 +5234,9 @@
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",
@@ -5611,17 +5688,39 @@
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 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 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.
@@ -5979,9 +6078,7 @@
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();
@@ -5990,7 +6087,7 @@
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;
}
@@ -6029,43 +6126,38 @@
}
}
- 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) {
@@ -6078,7 +6170,7 @@
return;
}
- auto p = layer;
+ sp<Layer> p = layer;
while (p != nullptr) {
if (excludeLayers.count(p) != 0) {
return;
@@ -6617,74 +6709,6 @@
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);
@@ -6778,11 +6802,11 @@
}
void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
- const sp<IBinder>& parent, const wp<Layer> parentLayer,
- const wp<IBinder>& producer, bool addToRoot) {
+ const wp<Layer> parent, const wp<IBinder>& producer,
+ bool addToRoot) {
Mutex::Autolock lock(mCreatedLayersLock);
mCreatedLayers[handle->localBinder()] =
- std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer, addToRoot);
+ std::make_unique<LayerCreatedState>(layer, parent, producer, addToRoot);
}
auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
@@ -6820,19 +6844,16 @@
}
sp<Layer> parent;
- bool allowAddRoot = state->addToRoot;
+ bool addToRoot = state->addToRoot;
if (state->initialParent != nullptr) {
- parent = fromHandle(state->initialParent).promote();
+ parent = state->initialParent.promote();
if (parent == nullptr) {
- ALOGE("Invalid parent %p", state->initialParent.get());
- allowAddRoot = false;
+ ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
+ addToRoot = false;
}
- } else if (state->initialParentLayer != nullptr) {
- parent = state->initialParentLayer.promote();
- allowAddRoot = false;
}
- if (parent == nullptr && allowAddRoot) {
+ if (parent == nullptr && addToRoot) {
layer->setIsAtRoot(true);
mCurrentState.layersSortedByZ.add(layer);
} else if (parent == nullptr) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 276c7f6..bf628dc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -135,6 +135,8 @@
eTransactionMask = 0x1f,
};
+enum class LatchUnsignaledConfig { Always, Auto, Disabled };
+
using DisplayColorSetting = compositionengine::OutputColorSetting;
struct SurfaceFlingerBE {
@@ -246,18 +248,13 @@
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;
@@ -326,6 +323,7 @@
// 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
@@ -601,7 +599,6 @@
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 +646,7 @@
// 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;
@@ -751,7 +748,14 @@
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);
@@ -795,8 +799,8 @@
// 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 sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
- bool addToRoot, uint32_t* outTransformHint);
+ const wp<Layer>& parentLayer, bool addToRoot,
+ uint32_t* outTransformHint);
// Traverse through all the layers and compute and cache its bounds.
void computeLayerBounds();
@@ -952,10 +956,6 @@
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;
@@ -1067,8 +1067,6 @@
return doDump(fd, args, asProto);
}
- void onFrameRateFlexibilityTokenReleased();
-
static mat4 calculateColorMatrix(float saturation);
void updateColorMatrixLocked();
@@ -1242,7 +1240,7 @@
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,28 +1333,22 @@
// 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 sp<IBinder>& parent,
- const wp<Layer> parentLayer, const wp<IBinder>& producer, bool addToRoot)
+ LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent,
+ const wp<IBinder>& producer, bool addToRoot)
: layer(layer),
initialParent(parent),
- initialParentLayer(parentLayer),
initialProducer(producer),
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.
- sp<IBinder> initialParent;
- wp<Layer> initialParentLayer;
+ wp<Layer> initialParent;
// Indicates the initial graphic buffer producer of the created layer, only used for
// creating layer in SurfaceFlinger.
wp<IBinder> initialProducer;
@@ -1370,8 +1362,7 @@
// thread.
std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
- const sp<IBinder>& parent, const wp<Layer> parentLayer,
- const wp<IBinder>& producer, bool addToRoot);
+ const wp<Layer> parent, const wp<IBinder>& producer, bool addToRoot);
auto getLayerCreatedState(const sp<IBinder>& handle);
sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index a8117f7..16f6e31 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -304,14 +304,6 @@
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 ed18260..8d0e426 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -88,8 +88,6 @@
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.h b/services/surfaceflinger/SurfaceTracing.h
index cea1a33..97adb20 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -77,7 +77,7 @@
private:
class Runner;
- static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
+ static constexpr auto DEFAULT_BUFFER_SIZE = 20_MB;
static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
SurfaceFlinger& mFlinger;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index f3d46ea..418fbc5 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -74,10 +74,10 @@
}
}
-void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) {
+void TransactionCallbackInvoker::addEmptyCallback(const ListenerCallbacks& listenerCallbacks) {
auto& [listener, callbackIds] = listenerCallbacks;
- auto& transactionStatsDeque = mCompletedTransactions[listener];
- transactionStatsDeque.emplace_back(callbackIds);
+ TransactionStats* transactionStats;
+ findOrCreateTransactionStats(listener, callbackIds, &transactionStats);
}
status_t TransactionCallbackInvoker::addOnCommitCallbackHandles(
@@ -116,7 +116,7 @@
return NO_ERROR;
}
-status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
+status_t TransactionCallbackInvoker::addUnpresentedCallbackHandle(
const sp<CallbackHandle>& handle) {
return addCallbackHandle(handle, std::vector<JankData>());
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index e203d41..6f67947 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -71,8 +71,10 @@
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
- status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
- void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks);
+ status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+ // Adds the callback handles for empty transactions or for non-buffer layer updates which do not
+ // include layer stats.
+ void addEmptyCallback(const ListenerCallbacks& listenerCallbacks);
void addPresentFence(const sp<Fence>& presentFence);
@@ -81,14 +83,12 @@
mCompletedTransactions.clear();
}
- status_t addCallbackHandle(const sp<CallbackHandle>& handle,
- const std::vector<JankData>& jankData);
-
-
private:
status_t findOrCreateTransactionStats(const sp<IBinder>& listener,
const std::vector<CallbackId>& callbackIds,
TransactionStats** outTransactionStats);
+ status_t addCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData);
std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
mCompletedTransactions;
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h
index 935502a..802d22d 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 @@
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 ecd797a..4e08393 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 @@
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/services/surfaceflinger/WpHash.h b/services/surfaceflinger/WpHash.h
new file mode 100644
index 0000000..86777b7
--- /dev/null
+++ b/services/surfaceflinger/WpHash.h
@@ -0,0 +1,25 @@
+/*
+ * 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
+
+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/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 673239d..caeff4a 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -63,18 +63,17 @@
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 78f8a2f..7702ea2 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -414,16 +414,6 @@
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 9c567d6..bf1e7e2 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -152,10 +152,6 @@
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 ba60a7d..640b9fb 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -136,10 +136,6 @@
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 9cf7c09..d192a2d 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -52,17 +52,6 @@
}
};
-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 91a5b52..7beba15 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -1067,7 +1067,7 @@
}
// b202394221
-TEST_F(LayerCallbackTest, DISABLED_NonBufferLayerStateChanges) {
+TEST_F(LayerCallbackTest, NonBufferLayerStateChanges) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createColorLayer("ColorLayer", Color::RED));
@@ -1085,4 +1085,47 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
+class TimedCallbackHelper {
+public:
+ static void function(void* callbackContext, nsecs_t, const sp<Fence>&,
+ const std::vector<SurfaceControlStats>&) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context");
+ }
+ TimedCallbackHelper* helper = static_cast<TimedCallbackHelper*>(callbackContext);
+ std::lock_guard lock(helper->mMutex);
+ helper->mInvokedTime = systemTime();
+ helper->mCv.notify_all();
+ }
+
+ void waitForCallback() {
+ std::unique_lock lock(mMutex);
+ ASSERT_TRUE(mCv.wait_for(lock, std::chrono::seconds(3), [&] { return mInvokedTime != -1; }))
+ << "did not receive callback";
+ }
+ void* getContext() { return static_cast<void*>(this); }
+
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ nsecs_t mInvokedTime = -1;
+};
+
+TEST_F(LayerCallbackTest, EmptyTransactionCallbackOrder) {
+ TimedCallbackHelper onCommitCallback;
+ TimedCallbackHelper onCompleteCallback;
+
+ // Add transaction callback before on commit callback
+ Transaction()
+ .addTransactionCompletedCallback(onCompleteCallback.function,
+ onCompleteCallback.getContext())
+ .addTransactionCommittedCallback(onCommitCallback.function,
+ onCommitCallback.getContext())
+ .apply();
+
+ EXPECT_NO_FATAL_FAILURE(onCompleteCallback.waitForCallback());
+ EXPECT_NO_FATAL_FAILURE(onCommitCallback.waitForCallback());
+ // verify we get the oncomplete at the same time or after the oncommit callback.
+ EXPECT_GE(onCompleteCallback.mInvokedTime, onCommitCallback.mInvokedTime);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 3ec6da9..a921aa8 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -273,6 +273,61 @@
}
}
+// 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 95301b3..f9b3185 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -37,6 +37,8 @@
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 @@
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, 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 @@
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/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 28d0222..67a0d7e 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -28,6 +28,7 @@
#include "AsyncCallRecorder.h"
#include "DisplayHardware/DisplayMode.h"
+#include "FrameTimeline.h"
#include "Scheduler/EventThread.h"
using namespace std::chrono_literals;
@@ -96,6 +97,8 @@
ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
+ void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+ nsecs_t preferredDeadline);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -120,6 +123,7 @@
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
sp<MockEventThreadConnection> mThrottledConnection;
+ std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
static constexpr uid_t mConnectionUid = 443;
static constexpr uid_t mThrottledConnectionUid = 177;
@@ -173,8 +177,8 @@
return VSYNC_PERIOD.count();
};
- mThread = std::make_unique<impl::EventThread>(std::move(source),
- /*tokenManager=*/nullptr,
+ mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
+ mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
mInterceptVSyncCallRecorder.getInvocable(),
throttleVsync, getVsyncPeriod);
@@ -247,6 +251,45 @@
expectedCount);
}
+void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+ nsecs_t preferredDeadline) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+ << 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)
+ << "Deadline timestamp out of order for frame timeline " << i;
+ EXPECT_GT(event.vsync.frameTimelines[i].expectedVSyncTimestamp,
+ event.vsync.frameTimelines[i - 1].expectedVSyncTimestamp)
+ << "Expected vsync timestamp out of order for frame timeline " << i;
+ }
+ if (event.vsync.frameTimelines[i].deadlineTimestamp == preferredDeadline) {
+ EXPECT_EQ(i, event.vsync.preferredFrameTimelineIndex)
+ << "Preferred frame timeline index should be " << i;
+ // For the platform-preferred frame timeline, the vsync ID is 0 because the first frame
+ // timeline is made before the rest.
+ EXPECT_EQ(0, event.vsync.frameTimelines[i].vsyncId)
+ << "Vsync ID incorrect for frame timeline " << i;
+ } else {
+ // Vsync ID 0 is used for the preferred frame timeline.
+ EXPECT_EQ(i + 1, event.vsync.frameTimelines[i].vsyncId)
+ << "Vsync ID incorrect for frame timeline " << i;
+ }
+ }
+}
+
void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected) {
auto args = mConnectionEventCallRecorder.waitForCall();
@@ -344,6 +387,19 @@
expectVSyncSetEnabledCallReceived(false);
}
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection);
+
+ expectVSyncSetEnabledCallReceived(true);
+
+ // Use the received callback to signal a vsync event.
+ // The interceptor should receive the event, as well as the connection.
+ mCallback->onVSyncEvent(123, 456, 789);
+ expectInterceptCallReceived(123);
+ expectVsyncEventFrameTimelinesCorrect(123, 789);
+}
+
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 3b40965..d021178 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -122,8 +122,6 @@
::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>());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index dd20d0d..8cca6af 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -289,7 +289,7 @@
}
static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
- layer->mDrawingParent = drawingParent.get();
+ layer->mDrawingParent = drawingParent;
}
/* ------------------------------------------------------------------------
@@ -375,6 +375,7 @@
auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+ auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
@@ -445,7 +446,6 @@
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) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 05551b4..8caadfb 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -24,8 +24,8 @@
#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"
@@ -74,6 +74,13 @@
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),
@@ -88,6 +95,10 @@
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,6 +135,15 @@
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);
@@ -245,6 +265,188 @@
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;
@@ -327,4 +529,216 @@
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/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index ddaa5a1..cae7684 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -175,6 +175,11 @@
void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
+ void expectSize(uint32_t width, uint32_t height) {
+ EXPECT_EQ(width, mOutBuffer->getWidth());
+ EXPECT_EQ(height, mOutBuffer->getHeight());
+ }
+
explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
if (mOutBuffer) {
mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc
index 0de0f9e..1612743 100644
--- a/services/vr/virtual_touchpad/virtual_touchpad.rc
+++ b/services/vr/virtual_touchpad/virtual_touchpad.rc
@@ -2,4 +2,4 @@
class core
user system
group system input uhid
- writepid /dev/cpuset/system/tasks
+ task_profiles VrServiceCapacityNormal