diff options
70 files changed, 1590 insertions, 477 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index e3c4edebbd..01c4723f29 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -265,6 +265,22 @@ on late-init chmod 0666 /sys/kernel/tracing/per_cpu/cpu14/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu16/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu16/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu17/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu17/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu18/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu18/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu19/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu19/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu20/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu20/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu21/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu21/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu22/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu22/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu23/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu23/trace # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created @@ -337,6 +353,22 @@ on post-fs-data && property:persist.mm_events.enabled=true chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu14/trace chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu15/trace chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu15/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu16/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu16/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu17/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu17/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu18/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu18/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu19/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu19/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu20/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu20/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu21/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu21/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu22/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu22/trace + chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace + chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace on property:persist.debug.atrace.boottrace=1 start boottrace diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 83e6787ebf..3722383e9e 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -38,11 +38,6 @@ #include "DumpPool.h" #include "TaskQueue.h" -// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to -// std::vector<std::string> -// TODO: remove once not used -#define MAX_ARGS_ARRAY_SIZE 1000 - // TODO: move everything under this namespace // TODO: and then remove explicitly android::os::dumpstate:: prefixes namespace android { diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 5082eb0694..39ef0b5aac 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -25,6 +25,7 @@ #include <functional> #include <inttypes.h> #include <regex> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/capability.h> @@ -281,36 +282,31 @@ status_t InstalldNativeService::start() { } status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) { - auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd)); const binder::Status dump_permission = checkPermission(kDump); if (!dump_permission.isOk()) { - out << dump_permission.toString8() << endl; + dprintf(fd, "%s\n", dump_permission.toString8().c_str()); return PERMISSION_DENIED; } - std::lock_guard<std::recursive_mutex> lock(mLock); - out << "installd is happy!" << endl; + std::lock_guard<std::recursive_mutex> lock(mLock); { std::lock_guard<std::recursive_mutex> lock(mMountsLock); - out << endl << "Storage mounts:" << endl; + dprintf(fd, "Storage mounts:\n"); for (const auto& n : mStorageMounts) { - out << " " << n.first << " = " << n.second << endl; + dprintf(fd, " %s = %s\n", n.first.c_str(), n.second.c_str()); } } { std::lock_guard<std::recursive_mutex> lock(mQuotasLock); - out << endl << "Per-UID cache quotas:" << endl; + dprintf(fd, "Per-UID cache quotas:\n"); for (const auto& n : mCacheQuotas) { - out << " " << n.first << " = " << n.second << endl; + dprintf(fd, " %d = %" PRId64 "\n", n.first, n.second); } } - out << "is_dexopt_blocked:" << android::installd::is_dexopt_blocked() << endl; - - out << endl; - out.flush(); + dprintf(fd, "is_dexopt_blocked:%d\n", android::installd::is_dexopt_blocked()); return NO_ERROR; } @@ -426,9 +422,131 @@ static bool prepare_app_profile_dir(const std::string& packageName, int32_t appI return true; } +static bool chown_app_dir(const std::string& path, uid_t uid, uid_t previousUid, gid_t cacheGid) { + FTS* fts; + char *argv[] = { (char*) path.c_str(), nullptr }; + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { + return false; + } + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + if (p->fts_info == FTS_D && p->fts_level == 1 + && (strcmp(p->fts_name, "cache") == 0 + || strcmp(p->fts_name, "code_cache") == 0)) { + // Mark cache dirs + p->fts_number = 1; + } else { + // Inherit parent's number + p->fts_number = p->fts_parent->fts_number; + } + + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (p->fts_statp->st_uid == previousUid) { + if (lchown(p->fts_path, uid, p->fts_number ? cacheGid : uid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + } else { + LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected UID " + << p->fts_statp->st_uid << " instead of " << previousUid; + } + break; + } + } + fts_close(fts); + return true; +} + +static void chown_app_profile_dir(const std::string &packageName, int32_t appId, int32_t userId) { + uid_t uid = multiuser_get_uid(userId, appId); + gid_t sharedGid = multiuser_get_shared_gid(userId, appId); + + const std::string profile_dir = + create_primary_current_profile_package_dir_path(userId, packageName); + char *argv[] = { (char*) profile_dir.c_str(), nullptr }; + if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) { + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (lchown(p->fts_path, uid, uid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + break; + } + } + fts_close(fts); + } + + const std::string ref_profile_path = + create_primary_reference_profile_package_dir_path(packageName); + argv[0] = (char *) ref_profile_path.c_str(); + if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) { + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + if (p->fts_info == FTS_D && p->fts_level == 0) { + if (chown(p->fts_path, AID_SYSTEM, sharedGid) != 0) { + PLOG(WARNING) << "Failed to chown " << p->fts_path; + } + continue; + } + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (lchown(p->fts_path, sharedGid, sharedGid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + break; + } + } + fts_close(fts); + } +} + +static binder::Status createAppDataDirs(const std::string& path, + int32_t uid, int32_t* previousUid, int32_t cacheGid, + const std::string& seInfo, mode_t targetMode) { + struct stat st{}; + bool existing = (stat(path.c_str(), &st) == 0); + if (existing) { + if (*previousUid < 0) { + // If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner + // of the directory as previousUid. This is required because it is not always possible + // to chown app data during app upgrade (e.g. secondary users' CE storage not unlocked) + *previousUid = st.st_uid; + } + if (*previousUid != uid) { + if (!chown_app_dir(path, uid, *previousUid, cacheGid)) { + return error("Failed to chown " + path); + } + } + } + + if (prepare_app_dir(path, targetMode, uid) || + prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || + prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { + return error("Failed to prepare " + path); + } + + // Consider restorecon over contents if label changed + if (restorecon_app_data_lazy(path, seInfo, uid, existing) || + restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || + restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { + return error("Failed to restorecon " + path); + } + + return ok(); +} + binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { + int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, + int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -441,6 +559,14 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str if (_aidl_return != nullptr) *_aidl_return = -1; int32_t uid = multiuser_get_uid(userId, appId); + + // If previousAppId < 0, we will use the existing app data owner as previousAppUid + // If previousAppId == 0, we use uid as previousUid (no data migration will happen) + // if previousAppId > 0, an app is upgrading and changing its app ID + int32_t previousUid = previousAppId > 0 + ? (int32_t) multiuser_get_uid(userId, previousAppId) + : (previousAppId == 0 ? uid : -1); + int32_t cacheGid = multiuser_get_cache_gid(userId, appId); mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; @@ -451,19 +577,13 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); - bool existing = (access(path.c_str(), F_OK) == 0); - if (prepare_app_dir(path, targetMode, uid) || - prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || - prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { - return error("Failed to prepare " + path); + auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode); + if (!status.isOk()) { + return status; } - - // Consider restorecon over contents if label changed - if (restorecon_app_data_lazy(path, seInfo, uid, existing) || - restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || - restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { - return error("Failed to restorecon " + path); + if (previousUid != uid) { + chown_app_profile_dir(packageName, appId, userId); } // Remember inode numbers of cache directories so that we can clear @@ -485,19 +605,10 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); - bool existing = (access(path.c_str(), F_OK) == 0); - - if (prepare_app_dir(path, targetMode, uid) || - prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || - prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { - return error("Failed to prepare " + path); - } - // Consider restorecon over contents if label changed - if (restorecon_app_data_lazy(path, seInfo, uid, existing) || - restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || - restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { - return error("Failed to restorecon " + path); + auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode); + if (!status.isOk()) { + return status; } if (!prepare_app_profile_dir(packageName, appId, userId)) { @@ -507,7 +618,6 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str return ok(); } - binder::Status InstalldNativeService::createAppData( const android::os::CreateAppDataArgs& args, android::os::CreateAppDataResult* _aidl_return) { @@ -516,7 +626,7 @@ binder::Status InstalldNativeService::createAppData( int64_t ceDataInode = -1; auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId, - args.seInfo, args.targetSdkVersion, &ceDataInode); + args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode); _aidl_return->ceDataInode = ceDataInode; _aidl_return->exceptionCode = status.exceptionCode(); _aidl_return->exceptionMessage = status.exceptionMessage(); @@ -530,7 +640,7 @@ binder::Status InstalldNativeService::createAppDataBatched( std::lock_guard<std::recursive_mutex> lock(mLock); std::vector<android::os::CreateAppDataResult> results; - for (auto arg : args) { + for (const auto &arg : args) { android::os::CreateAppDataResult result; createAppData(arg, &result); results.push_back(result); @@ -628,14 +738,11 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri } } if (flags & FLAG_STORAGE_DE) { - std::string suffix = ""; - bool only_cache = false; + std::string suffix; if (flags & FLAG_CLEAR_CACHE_ONLY) { suffix = CACHE_DIR_POSTFIX; - only_cache = true; } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { suffix = CODE_CACHE_DIR_POSTFIX; - only_cache = true; } auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix; @@ -1230,7 +1337,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId, - seInfo, targetSdkVersion, nullptr).isOk()) { + /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) { res = error("Failed to create package target"); goto fail; } diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index ae257dfcf8..8cfda010a8 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -47,7 +47,8 @@ public: binder::Status createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); + int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, + int64_t* _aidl_return); binder::Status createAppData( const android::os::CreateAppDataArgs& args, diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl index 96d7faaaa2..d5e8ee5daf 100644 --- a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl +++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl @@ -23,6 +23,7 @@ parcelable CreateAppDataArgs { int userId; int flags; int appId; + int previousAppId; @utf8InCpp String seInfo; int targetSdkVersion; } diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index ea26955af5..a937436f9c 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -287,6 +287,7 @@ protected: kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode_); @@ -1257,6 +1258,7 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode_)); @@ -1320,6 +1322,7 @@ class BootProfileTest : public ProfileTest { kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode)); diff --git a/cmds/rss_hwm_reset/rss_hwm_reset.rc b/cmds/rss_hwm_reset/rss_hwm_reset.rc index fbbc8200b2..271cbf89f4 100644 --- a/cmds/rss_hwm_reset/rss_hwm_reset.rc +++ b/cmds/rss_hwm_reset/rss_hwm_reset.rc @@ -18,7 +18,7 @@ service rss_hwm_reset /system/bin/rss_hwm_reset 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/Android.bp b/cmds/service/Android.bp index 3e8e3f67f8..21ac11b4cf 100644 --- a/cmds/service/Android.bp +++ b/cmds/service/Android.bp @@ -52,3 +52,21 @@ cc_binary { "-Werror", ], } + +cc_binary_host { + name: "aservice", + + srcs: ["service.cpp"], + + shared_libs: [ + "libcutils", + "libutils", + "libbinder", + ], + + cflags: [ + "-DXP_UNIX", + "-Wall", + "-Werror", + ], +} diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index 18b6b58a9e..fe417a362f 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -45,37 +45,14 @@ void writeString16(Parcel& parcel, const char* string) } } -// get the name of the generic interface we hold a reference to -static String16 get_interface_name(sp<IBinder> service) -{ - if (service != nullptr) { - Parcel data, reply; - 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) @@ -87,7 +64,7 @@ int main(int argc, char* const argv[]) wantsUsage = true; break; default: - aerr << "service: Unknown option -" << ic << endl; + aerr << prog_name << ": Unknown option -" << ic << endl; wantsUsage = true; result = 10; break; @@ -96,10 +73,13 @@ int main(int argc, char* const argv[]) #ifdef VENDORSERVICES ProcessState::initWithDriver("/dev/vndbinder"); #endif +#ifndef __ANDROID__ + setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1})); +#endif 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; } @@ -113,7 +93,7 @@ int main(int argc, char* const argv[]) 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; } @@ -125,8 +105,8 @@ int main(int argc, char* const argv[]) String16 name = services[i]; sp<IBinder> service = sm->checkService(name); aout << i - << "\t" << good_old_string(name) - << ": [" << good_old_string(get_interface_name(service)) << "]" + << "\t" << name + << ": [" << (service ? service->getInterfaceDescriptor() : String16()) << "]" << endl; } } else if (strcmp(argv[optind], "call") == 0) { @@ -134,10 +114,11 @@ int main(int argc, char* const argv[]) if (optind+1 < argc) { int serviceArg = optind; sp<IBinder> service = sm->checkService(String16(argv[optind++])); - String16 ifName = get_interface_name(service); + String16 ifName = (service ? service->getInterfaceDescriptor() : String16()); int32_t code = atoi(argv[optind++]); if (service != nullptr && ifName.size() > 0) { Parcel data, reply; + data.markForBinder(service); // the interface name is first data.writeInterfaceToken(ifName); @@ -147,7 +128,7 @@ int main(int argc, char* const argv[]) 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; @@ -156,7 +137,7 @@ int main(int argc, char* const argv[]) } 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; @@ -165,7 +146,7 @@ int main(int argc, char* const argv[]) } 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; @@ -174,7 +155,7 @@ int main(int argc, char* const argv[]) } 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; @@ -183,7 +164,7 @@ int main(int argc, char* const argv[]) } 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; @@ -195,7 +176,7 @@ int main(int argc, char* const argv[]) } 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; @@ -203,7 +184,7 @@ int main(int argc, char* const argv[]) 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; @@ -212,7 +193,7 @@ int main(int argc, char* const argv[]) } 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; @@ -221,7 +202,8 @@ int main(int argc, char* const argv[]) 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; @@ -229,13 +211,14 @@ int main(int argc, char* const argv[]) int afd = ashmem_create_region("test", statbuf.st_size); void* ptr = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, afd, 0); - read(fd, ptr, statbuf.st_size); + (void)read(fd, ptr, statbuf.st_size); close(fd); data.writeFileDescriptor(afd, true /* take ownership */); } 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; @@ -322,7 +305,7 @@ int main(int argc, char* const argv[]) // 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; @@ -332,44 +315,44 @@ int main(int argc, char* const argv[]) 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 6d5070fa04..0dd29e05ba 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -9,5 +9,5 @@ service servicemanager /system/bin/servicemanager 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 756f6c3bc8..c9305a1c60 100644 --- a/cmds/servicemanager/vndservicemanager.rc +++ b/cmds/servicemanager/vndservicemanager.rc @@ -2,7 +2,7 @@ service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder 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/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp index b74923cbfc..ac3e5b80fd 100644 --- a/libs/android_runtime_lazy/Android.bp +++ b/libs/android_runtime_lazy/Android.bp @@ -42,12 +42,13 @@ package { cc_library { name: "libandroid_runtime_lazy", vendor_available: true, + recovery_available: true, double_loadable: true, host_supported: true, target: { darwin: { enabled: false, - } + }, }, cflags: [ diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 7ff128e4a0..8270ae5ce7 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -25,6 +25,7 @@ cc_library_headers { name: "libbinder_headers", export_include_dirs: ["include"], vendor_available: true, + recovery_available: true, host_supported: true, // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, @@ -75,6 +76,7 @@ cc_library { vndk: { enabled: true, }, + recovery_available: true, double_loadable: true, host_supported: true, // TODO(b/153609531): remove when no longer needed. @@ -158,10 +160,6 @@ cc_library { export_aidl_headers: true, }, - // TODO(b/142684679): for com.android.media which is compiled - // as vendor and used as system code. - use_apex_name_macro: true, - cflags: [ "-Wall", "-Wextra", @@ -340,6 +338,7 @@ cc_library { "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 d3eef4e88e..ec9d5544f9 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -547,7 +547,6 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, 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/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index aff9e0d48d..81e61daae1 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -448,21 +448,27 @@ std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnection // on-device service manager. class ServiceManagerHostShim : public ServiceManagerShim { public: - using ServiceManagerShim::ServiceManagerShim; + ServiceManagerHostShim(const sp<AidlServiceManager>& impl, + const RpcDelegateServiceManagerOptions& options) + : ServiceManagerShim(impl), mOptions(options) {} // ServiceManagerShim::getService is based on checkService, so no need to override it. sp<IBinder> checkService(const String16& name) const override { - return getDeviceService({String8(name).c_str()}); + return getDeviceService({String8(name).c_str()}, mOptions); } protected: // Override realGetService for ServiceManagerShim::waitForService. Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) { - *_aidl_return = getDeviceService({"-g", name}); + *_aidl_return = getDeviceService({"-g", name}, mOptions); return Status::ok(); } + +private: + RpcDelegateServiceManagerOptions mOptions; }; -sp<IServiceManager> createRpcDelegateServiceManager() { - auto binder = getDeviceService({"manager"}); +sp<IServiceManager> createRpcDelegateServiceManager( + const RpcDelegateServiceManagerOptions& options) { + auto binder = getDeviceService({"manager"}, options); if (binder == nullptr) { ALOGE("getDeviceService(\"manager\") returns null"); return nullptr; @@ -472,7 +478,7 @@ sp<IServiceManager> createRpcDelegateServiceManager() { ALOGE("getDeviceService(\"manager\") returns non service manager"); return nullptr; } - return sp<ServiceManagerHostShim>::make(interface); + return sp<ServiceManagerHostShim>::make(interface, options); } #endif diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS index 350994a86b..f954e74eba 100644 --- a/libs/binder/OWNERS +++ b/libs/binder/OWNERS @@ -1,5 +1,6 @@ -arve@google.com +# Bug component: 32456 ctate@google.com hackbod@google.com maco@google.com smoreland@google.com +tkjos@google.com diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 07555f6eee..8f4f0f0016 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -98,9 +98,8 @@ enum { BLOB_ASHMEM_MUTABLE = 2, }; -static void acquire_object(const sp<ProcessState>& proc, - const flat_binder_object& obj, const void* who, size_t* outAshmemSize) -{ +static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, + const void* who) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { @@ -117,13 +116,6 @@ static void acquire_object(const sp<ProcessState>& proc, return; } case BINDER_TYPE_FD: { - if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { - // If we own an ashmem fd, keep track of how much memory it refers to. - int size = ashmem_get_size_region(obj.handle); - if (size > 0) { - *outAshmemSize += size; - } - } return; } } @@ -131,9 +123,8 @@ static void acquire_object(const sp<ProcessState>& proc, ALOGD("Invalid object type 0x%08x", obj.hdr.type); } -static void release_object(const sp<ProcessState>& proc, - const flat_binder_object& obj, const void* who, size_t* outAshmemSize) -{ +static void release_object(const sp<ProcessState>& proc, const flat_binder_object& obj, + const void* who) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { @@ -151,16 +142,6 @@ static void release_object(const sp<ProcessState>& proc, } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned - if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { - int size = ashmem_get_size_region(obj.handle); - if (size > 0) { - // ashmem size might have changed since last time it was accounted for, e.g. - // in acquire_object(). Value of *outAshmemSize is not critical since we are - // releasing the object anyway. Check for integer overflow condition. - *outAshmemSize -= std::min(*outAshmemSize, static_cast<size_t>(size)); - } - } - close(obj.handle); } return; @@ -430,8 +411,9 @@ status_t Parcel::setData(const uint8_t* buffer, size_t len) status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) { - if (parcel->isForRpc() != isForRpc()) { - ALOGE("Cannot append Parcel of one format to another."); + if (mSession != parcel->mSession) { + ALOGE("Cannot append Parcel from one context to another. They may be different formats, " + "and objects are specific to a context."); return BAD_TYPE; } @@ -512,7 +494,7 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off); - acquire_object(proc, *flat, this, &mOpenAshmemSize); + acquire_object(proc, *flat, this); if (flat->hdr.type == BINDER_TYPE_FD) { // If this is a file descriptor, we need to dup it so the @@ -539,6 +521,25 @@ int Parcel::compareData(const Parcel& other) { return memcmp(data(), other.data(), size); } +status_t Parcel::compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset, + size_t len, int* result) const { + if (len > INT32_MAX || thisOffset > INT32_MAX || otherOffset > INT32_MAX) { + // Don't accept size_t values which may have come from an inadvertent conversion from a + // negative int. + return BAD_VALUE; + } + size_t thisLimit; + if (__builtin_add_overflow(thisOffset, len, &thisLimit) || thisLimit > mDataSize) { + return BAD_VALUE; + } + size_t otherLimit; + if (__builtin_add_overflow(otherOffset, len, &otherLimit) || otherLimit > other.mDataSize) { + return BAD_VALUE; + } + *result = memcmp(data() + thisOffset, other.data() + otherOffset, len); + return NO_ERROR; +} + bool Parcel::allowFds() const { return mAllowFds; @@ -566,6 +567,33 @@ bool Parcel::hasFileDescriptors() const return mHasFds; } +status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const { + if (len > INT32_MAX || offset > INT32_MAX) { + // Don't accept size_t values which may have come from an inadvertent conversion from a + // negative int. + return BAD_VALUE; + } + size_t limit; + if (__builtin_add_overflow(offset, len, &limit) || limit > mDataSize) { + return BAD_VALUE; + } + *result = false; + for (size_t i = 0; i < mObjectsSize; i++) { + size_t pos = mObjects[i]; + if (pos < offset) continue; + if (pos + sizeof(flat_binder_object) > offset + len) { + if (mObjectsSorted) break; + else continue; + } + const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos); + if (flat->hdr.type == BINDER_TYPE_FD) { + *result = true; + break; + } + } + return NO_ERROR; +} + void Parcel::markSensitive() const { mDeallocZero = true; @@ -602,6 +630,8 @@ void Parcel::updateWorkSourceRequestHeaderPosition() const { #if defined(__ANDROID_VNDK__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); +#elif defined(__ANDROID_RECOVERY__) +constexpr int32_t kHeader = B_PACK_CHARS('R', 'E', 'C', 'O'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); #endif @@ -1335,7 +1365,7 @@ restart_write: // Need to write meta-data? if (nullMetaData || val.binder != 0) { mObjects[mObjectsSize] = mDataPos; - acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize); + acquire_object(ProcessState::self(), val, this); mObjectsSize++; } @@ -2225,7 +2255,7 @@ void Parcel::releaseObjects() i--; const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data+objects[i]); - release_object(proc, *flat, this, &mOpenAshmemSize); + release_object(proc, *flat, this); } } @@ -2242,7 +2272,7 @@ void Parcel::acquireObjects() i--; const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data+objects[i]); - acquire_object(proc, *flat, this, &mOpenAshmemSize); + acquire_object(proc, *flat, this); } } @@ -2447,7 +2477,7 @@ status_t Parcel::continueWrite(size_t desired) // will need to rescan because we may have lopped off the only FDs mFdsKnown = false; } - release_object(proc, *flat, this, &mOpenAshmemSize); + release_object(proc, *flat, this); } if (objectsSize == 0) { @@ -2540,7 +2570,6 @@ void Parcel::initState() mAllowFds = true; mDeallocZero = false; mOwner = nullptr; - mOpenAshmemSize = 0; mWorkSourceRequestHeaderPosition = 0; mRequestHeaderPresent = false; @@ -2557,18 +2586,9 @@ void Parcel::initState() } } -void Parcel::scanForFds() const -{ - bool hasFds = false; - for (size_t i=0; i<mObjectsSize; i++) { - const flat_binder_object* flat - = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]); - if (flat->hdr.type == BINDER_TYPE_FD) { - hasFds = true; - break; - } - } - mHasFds = hasFds; +void Parcel::scanForFds() const { + status_t status = hasFileDescriptorsInRange(0, dataSize(), &mHasFds); + ALOGE_IF(status != NO_ERROR, "Error %d calling hasFileDescriptorsInRange()", status); mFdsKnown = true; } @@ -2576,13 +2596,28 @@ size_t Parcel::getBlobAshmemSize() const { // This used to return the size of all blobs that were written to ashmem, now we're returning // the ashmem currently referenced by this Parcel, which should be equivalent. - // TODO: Remove method once ABI can be changed. - return mOpenAshmemSize; + // TODO(b/202029388): Remove method once ABI can be changed. + return getOpenAshmemSize(); } size_t Parcel::getOpenAshmemSize() const { - return mOpenAshmemSize; + size_t openAshmemSize = 0; + for (size_t i = 0; i < mObjectsSize; i++) { + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]); + + // cookie is compared against zero for historical reasons + // > obj.cookie = takeOwnership ? 1 : 0; + if (flat->hdr.type == BINDER_TYPE_FD && flat->cookie != 0 && ashmem_valid(flat->handle)) { + int size = ashmem_get_size_region(flat->handle); + if (__builtin_add_overflow(openAshmemSize, size, &openAshmemSize)) { + ALOGE("Overflow when computing ashmem size."); + return SIZE_MAX; + } + } + } + return openAshmemSize; } // --- Parcel::Blob --- diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 94b28063ab..4f21cda4a1 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -89,13 +89,21 @@ sp<ProcessState> ProcessState::selfOrNull() return init(nullptr, false /*requireDefault*/); } +[[clang::no_destroy]] static sp<ProcessState> gProcess; +[[clang::no_destroy]] static std::mutex gProcessMutex; + +static void verifyNotForked(bool forked) { + LOG_ALWAYS_FATAL_IF(forked, "libbinder ProcessState can not be used after fork"); +} + sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) { - [[clang::no_destroy]] static sp<ProcessState> gProcess; - [[clang::no_destroy]] static std::mutex gProcessMutex; if (driver == nullptr) { std::lock_guard<std::mutex> l(gProcessMutex); + if (gProcess) { + verifyNotForked(gProcess->mForked); + } return gProcess; } @@ -106,6 +114,14 @@ sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) driver = "/dev/binder"; } + // we must install these before instantiating the gProcess object, + // otherwise this would race with creating it, and there could be the + // possibility of an invalid gProcess object forked by another thread + // before these are installed + int ret = pthread_atfork(ProcessState::onFork, ProcessState::parentPostFork, + ProcessState::childPostFork); + LOG_ALWAYS_FATAL_IF(ret != 0, "pthread_atfork error %s", strerror(ret)); + std::lock_guard<std::mutex> l(gProcessMutex); gProcess = sp<ProcessState>::make(driver); }); @@ -119,6 +135,7 @@ sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) gProcess->getDriverName().c_str(), driver); } + verifyNotForked(gProcess->mForked); return gProcess; } @@ -137,6 +154,24 @@ sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/) return context; } +void ProcessState::onFork() { + // make sure another thread isn't currently retrieving ProcessState + gProcessMutex.lock(); +} + +void ProcessState::parentPostFork() { + gProcessMutex.unlock(); +} + +void ProcessState::childPostFork() { + // another thread might call fork before gProcess is instantiated, but after + // the thread handler is installed + if (gProcess) { + gProcess->mForked = true; + } + gProcessMutex.unlock(); +} + void ProcessState::startThreadPool() { AutoMutex _l(mLock); @@ -426,6 +461,7 @@ ProcessState::ProcessState(const char* driver) mWaitingForThreads(0), mMaxThreads(DEFAULT_MAX_BINDER_THREADS), mStarvationStartTimeMs(0), + mForked(false), mThreadPoolStarted(false), mThreadPoolSeq(1), mCallRestriction(CallRestriction::NONE) { diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 967b8e3801..93ed50e986 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -58,10 +58,6 @@ sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTranspo return sp<RpcServer>::make(std::move(ctx)); } -void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() { - mAgreedExperimental = true; -} - status_t RpcServer::setupUnixDomainServer(const char* path) { return setupSocketServer(UnixSocketAddress(path)); } @@ -127,14 +123,23 @@ void RpcServer::setProtocolVersion(uint32_t version) { void RpcServer::setRootObject(const sp<IBinder>& binder) { std::lock_guard<std::mutex> _l(mLock); + mRootObjectFactory = nullptr; mRootObjectWeak = mRootObject = binder; } void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) { std::lock_guard<std::mutex> _l(mLock); mRootObject.clear(); + mRootObjectFactory = nullptr; mRootObjectWeak = binder; } +void RpcServer::setPerSessionRootObject( + std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& makeObject) { + std::lock_guard<std::mutex> _l(mLock); + mRootObject.clear(); + mRootObjectWeak.clear(); + mRootObjectFactory = std::move(makeObject); +} sp<IBinder> RpcServer::getRootObject() { std::lock_guard<std::mutex> _l(mLock); @@ -154,14 +159,12 @@ static void joinRpcServer(sp<RpcServer>&& thiz) { } 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); @@ -174,8 +177,14 @@ void RpcServer::join() { status_t status; while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) { - unique_fd clientFd(TEMP_FAILURE_RETRY( - accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC | SOCK_NONBLOCK))); + sockaddr_storage addr; + socklen_t addrLen = sizeof(addr); + + unique_fd clientFd( + TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr), + &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK))); + + LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address"); if (clientFd < 0) { ALOGE("Could not accept4 socket: %s", strerror(errno)); @@ -187,7 +196,7 @@ void RpcServer::join() { std::lock_guard<std::mutex> _l(mLock); std::thread thread = std::thread(&RpcServer::establishConnection, sp<RpcServer>::fromExisting(this), - std::move(clientFd)); + std::move(clientFd), addr, addrLen); mConnectingThreads[thread.get_id()] = std::move(thread); } } @@ -257,10 +266,8 @@ size_t RpcServer::numUninitializedSessions() { return mConnectingThreads.size(); } -void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) { - // TODO(b/183988761): cannot trust this simple ID - LOG_ALWAYS_FATAL_IF(!server->mAgreedExperimental, "no!"); - +void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, + const sockaddr_storage addr, socklen_t addrLen) { // 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); @@ -383,11 +390,23 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie session = RpcSession::make(); session->setMaxIncomingThreads(server->mMaxThreads); if (!session->setProtocolVersion(protocolVersion)) return; + + // if null, falls back to server root + sp<IBinder> sessionSpecificRoot; + if (server->mRootObjectFactory != nullptr) { + sessionSpecificRoot = + server->mRootObjectFactory(reinterpret_cast<const sockaddr*>(&addr), + addrLen); + if (sessionSpecificRoot == nullptr) { + ALOGE("Warning: server returned null from root object factory"); + } + } + if (!session->setForServer(server, sp<RpcServer::EventListener>::fromExisting( static_cast<RpcServer::EventListener*>( server.get())), - sessionId)) { + sessionId, sessionSpecificRoot)) { ALOGE("Failed to attach server to session"); return; } @@ -478,19 +497,16 @@ void RpcServer::onSessionIncomingThreadEnded() { } 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 9b7b5e1ceb..a5a2bb1017 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -93,6 +93,20 @@ size_t RpcSession::getMaxIncomingThreads() { return mMaxIncomingThreads; } +void RpcSession::setMaxOutgoingThreads(size_t threads) { + std::lock_guard<std::mutex> _l(mMutex); + LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(), + "Must set max outgoing threads before setting up connections, but has %zu " + "client(s) and %zu server(s)", + mConnections.mOutgoing.size(), mConnections.mIncoming.size()); + mMaxOutgoingThreads = threads; +} + +size_t RpcSession::getMaxOutgoingThreads() { + std::lock_guard<std::mutex> _l(mMutex); + return mMaxOutgoingThreads; +} + bool RpcSession::setProtocolVersion(uint32_t version) { if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT && version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { @@ -480,6 +494,12 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< return status; } + size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads); + ALOGI_IF(outgoingThreads != numThreadsAvailable, + "Server hints client to start %zu outgoing threads, but client will only start %zu " + "because it is preconfigured to start at most %zu outgoing threads.", + numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads); + // TODO(b/189955605): we should add additional sessions dynamically // instead of all at once - the other side should be responsible for setting // up additional connections. We need to create at least one (unless 0 are @@ -487,7 +507,10 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< // any requests at all. // we've already setup one client - for (size_t i = 0; i + 1 < numThreadsAvailable; i++) { + LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu " + "incoming threads", + outgoingThreads, numThreadsAvailable, mMaxIncomingThreads); + for (size_t i = 0; i + 1 < outgoingThreads; i++) { if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status; } @@ -672,7 +695,8 @@ status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTran status_t status = OK; if (init) { - mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this)); + status = + mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this)); } { @@ -684,7 +708,8 @@ status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTran } bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListener>& eventListener, - const std::vector<uint8_t>& sessionId) { + const std::vector<uint8_t>& sessionId, + const sp<IBinder>& sessionSpecificRoot) { LOG_ALWAYS_FATAL_IF(mForServer != nullptr); LOG_ALWAYS_FATAL_IF(server == nullptr); LOG_ALWAYS_FATAL_IF(mEventListener != nullptr); @@ -697,6 +722,7 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene mId = sessionId; mForServer = server; mEventListener = eventListener; + mSessionSpecificRootObject = sessionSpecificRoot; return true; } diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 9ba64f3407..09b3d68626 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -870,7 +870,9 @@ processTransactInternalTailCall: if (server) { switch (transaction->code) { case RPC_SPECIAL_TRANSACT_GET_ROOT: { - replyStatus = reply.writeStrongBinder(server->getRootObject()); + sp<IBinder> root = session->mSessionSpecificRootObject + ?: server->getRootObject(); + replyStatus = reply.writeStrongBinder(root); break; } default: { diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 50de22bc29..dba0a43060 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -60,20 +60,21 @@ public: 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/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp index 27cc563adc..194254ac69 100644 --- a/libs/binder/ServiceManagerHost.cpp +++ b/libs/binder/ServiceManagerHost.cpp @@ -124,7 +124,8 @@ void cleanupCommandResult(const void* id, void* obj, void* /* cookie */) { } // namespace -sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs) { +sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs, + const RpcDelegateServiceManagerOptions& options) { std::vector<std::string> prefix{"adb", "shell", "servicedispatcher"}; serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end()); @@ -158,6 +159,10 @@ sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs) { LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value()); auto rpcSession = RpcSession::make(); + if (options.maxOutgoingThreads.has_value()) { + rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads); + } + if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort()); status != OK) { ALOGE("Unable to set up inet client on host port %u: %s", *forwardResult->hostPort(), diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h index e59724c391..c5310dac20 100644 --- a/libs/binder/ServiceManagerHost.h +++ b/libs/binder/ServiceManagerHost.h @@ -21,11 +21,14 @@ namespace android { +struct RpcDelegateServiceManagerOptions; + // Get a service on device by running servicedispatcher with the given args, e.g. // getDeviceService({"foo"}); // Return nullptr on any error. // When the returned binder object is destroyed, remove adb forwarding and kills // the long-running servicedispatcher process. -sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs); +sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs, + const RpcDelegateServiceManagerOptions& options); } // namespace android diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl index ece79895f7..bffab5e86b 100644 --- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl +++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl @@ -27,4 +27,7 @@ parcelable StagedApexInfo { @utf8InCpp String diskImagePath; long versionCode; @utf8InCpp String versionName; + boolean hasBootClassPathJars; + boolean hasDex2OatBootClassPathJars; + boolean hasSystemServerClassPathJars; } diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index ff90b30380..7d14315b01 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -129,48 +129,50 @@ public: \ #endif -#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\ - const ::android::StaticString16 \ - I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\ - const ::android::String16 I##INTERFACE::descriptor( \ - I##INTERFACE##_descriptor_static_str16); \ - const ::android::String16& \ - I##INTERFACE::getInterfaceDescriptor() const { \ - return I##INTERFACE::descriptor; \ - } \ - ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ - const ::android::sp<::android::IBinder>& obj) \ - { \ - ::android::sp<I##INTERFACE> intr; \ - if (obj != nullptr) { \ - intr = ::android::sp<I##INTERFACE>::cast( \ - obj->queryLocalInterface(I##INTERFACE::descriptor)); \ - if (intr == nullptr) { \ - intr = ::android::sp<Bp##INTERFACE>::make(obj); \ - } \ - } \ - return intr; \ - } \ - std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl; \ - bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\ - { \ - /* Only one user of this interface can use this function */ \ - /* at a time. This is a heuristic to detect if two different */ \ - /* users in the same process use this function. */ \ - assert(!I##INTERFACE::default_impl); \ - if (impl) { \ - I##INTERFACE::default_impl = std::move(impl); \ - return true; \ - } \ - return false; \ - } \ - const std::unique_ptr<I##INTERFACE>& I##INTERFACE::getDefaultImpl() \ - { \ - return I##INTERFACE::default_impl; \ - } \ - I##INTERFACE::I##INTERFACE() { } \ - I##INTERFACE::~I##INTERFACE() { } \ - +// Macro to be used by both IMPLEMENT_META_INTERFACE and IMPLEMENT_META_NESTED_INTERFACE +#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(ITYPE, INAME, BPTYPE) \ + const ::android::String16& ITYPE::getInterfaceDescriptor() const { return ITYPE::descriptor; } \ + ::android::sp<ITYPE> ITYPE::asInterface(const ::android::sp<::android::IBinder>& obj) { \ + ::android::sp<ITYPE> intr; \ + if (obj != nullptr) { \ + intr = ::android::sp<ITYPE>::cast(obj->queryLocalInterface(ITYPE::descriptor)); \ + if (intr == nullptr) { \ + intr = ::android::sp<BPTYPE>::make(obj); \ + } \ + } \ + return intr; \ + } \ + std::unique_ptr<ITYPE> ITYPE::default_impl; \ + bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) { \ + /* Only one user of this interface can use this function */ \ + /* at a time. This is a heuristic to detect if two different */ \ + /* users in the same process use this function. */ \ + assert(!ITYPE::default_impl); \ + if (impl) { \ + ITYPE::default_impl = std::move(impl); \ + return true; \ + } \ + return false; \ + } \ + const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \ + ITYPE::INAME() {} \ + ITYPE::~INAME() {} + +// Macro for an interface type. +#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + const ::android::StaticString16 I##INTERFACE##_descriptor_static_str16( \ + __IINTF_CONCAT(u, NAME)); \ + const ::android::String16 I##INTERFACE::descriptor(I##INTERFACE##_descriptor_static_str16); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(I##INTERFACE, I##INTERFACE, Bp##INTERFACE) + +// Macro for "nested" interface type. +// For example, +// class Parent .. { class INested .. { }; }; +// DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(Parent, Nested, "Parent.INested") +#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(PARENT, INTERFACE, NAME) \ + const ::android::String16 PARENT::I##INTERFACE::descriptor(NAME); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(PARENT::I##INTERFACE, I##INTERFACE, \ + PARENT::Bp##INTERFACE) #define CHECK_INTERFACE(interface, data, reply) \ do { \ diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index a48075dad1..240e3c2b26 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -188,7 +188,16 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid, // // ... // } // Resources are cleaned up when the object is destroyed. -sp<IServiceManager> createRpcDelegateServiceManager(); +// +// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated. +// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked +// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads. +// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|. +struct RpcDelegateServiceManagerOptions { + std::optional<size_t> maxOutgoingThreads; +}; +sp<IServiceManager> createRpcDelegateServiceManager( + const RpcDelegateServiceManagerOptions& options); #endif } // namespace android diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index fd8ac622a5..8fb4a377d6 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -81,12 +81,15 @@ public: size_t start, size_t len); int compareData(const Parcel& other); + status_t compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset, + size_t length, int* result) const; bool allowFds() const; bool pushAllowFds(bool allowFds); void restoreAllowFds(bool lastValue); bool hasFileDescriptors() const; + status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const; // Zeros data when reallocating. Other mitigations may be added // in the future. @@ -1141,6 +1144,7 @@ private: release_func mOwner; sp<RpcSession> mSession; + size_t mReserved; class Blob { public: @@ -1225,13 +1229,19 @@ public: inline void* data() { return mData; } }; -private: - size_t mOpenAshmemSize; + /** + * Returns the total amount of ashmem memory owned by this object. + * + * Note: for historical reasons, this does not include ashmem memory which + * is referenced by this Parcel, but which this parcel doesn't own (e.g. + * writeFileDescriptor is called without 'takeOwnership' true). + */ + size_t getOpenAshmemSize() const; -public: - // TODO: Remove once ABI can be changed. +private: + // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference + // this size_t getBlobAshmemSize() const; - size_t getOpenAshmemSize() const; }; // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 72c2ab7b31..cf8d8e433c 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -94,6 +94,10 @@ public: private: static sp<ProcessState> init(const char* defaultDriver, bool requireDefault); + static void onFork(); + static void parentPostFork(); + static void childPostFork(); + friend class IPCThreadState; friend class sp<ProcessState>; @@ -132,6 +136,7 @@ private: Vector<handle_entry> mHandleToObject; + bool mForked; bool mThreadPoolStarted; volatile int32_t mThreadPoolSeq; diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index fb2cf23cd0..aaa812b067 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 @@ public: */ [[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd); - void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); - /** * This must be called before adding a client session. * @@ -130,6 +124,10 @@ public: * Holds a weak reference to the root object. */ void setRootObjectWeak(const wp<IBinder>& binder); + /** + * Allows a root object to be created for each session + */ + void setPerSessionRootObject(std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& object); sp<IBinder> getRootObject(); /** @@ -179,11 +177,11 @@ private: void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; - static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd); - status_t setupSocketServer(const RpcSocketAddress& address); + static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, + const sockaddr_storage addr, socklen_t addrLen); + [[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 @@ -194,6 +192,7 @@ private: std::map<std::thread::id, std::thread> mConnectingThreads; sp<IBinder> mRootObject; wp<IBinder> mRootObjectWeak; + std::function<sp<IBinder>(const sockaddr*, socklen_t)> mRootObjectFactory; std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions; std::unique_ptr<FdTrigger> mShutdownTrigger; std::condition_variable mShutdownCv; diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index e64572b234..1bc84641f2 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; @@ -50,6 +46,8 @@ constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIM */ class RpcSession final : public virtual RefBase { public: + static constexpr size_t kDefaultMaxOutgoingThreads = 10; + // Create an RpcSession with default configuration (raw sockets). static sp<RpcSession> make(); @@ -72,6 +70,18 @@ public: size_t getMaxIncomingThreads(); /** + * Set the maximum number of outgoing threads allowed to be made. + * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this + * connection as a client. + * + * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession + * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where + * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|. + */ + void setMaxOutgoingThreads(size_t threads); + size_t getMaxOutgoingThreads(); + + /** * By default, the minimum of the supported versions of the client and the * server will be used. Usually, this API should only be used for debugging. */ @@ -126,7 +136,7 @@ public: * 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 @@ -206,7 +216,7 @@ private: 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 @@ -242,12 +252,13 @@ private: bool init); [[nodiscard]] bool setForServer(const wp<RpcServer>& server, const wp<RpcSession::EventListener>& eventListener, - const std::vector<uint8_t>& sessionId); + const std::vector<uint8_t>& sessionId, + const sp<IBinder>& sessionSpecificRoot); sp<RpcConnection> assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport); [[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection); - status_t initShutdownTrigger(); + [[nodiscard]] status_t initShutdownTrigger(); enum class ConnectionUse { CLIENT, @@ -258,8 +269,8 @@ private: // 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; } @@ -299,6 +310,10 @@ private: sp<WaitForShutdownListener> mShutdownListener; // used for client sessions wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client + // session-specific root object (if a different root is used for each + // session) + sp<IBinder> mSessionSpecificRootObject; + std::vector<uint8_t> mId; std::unique_ptr<FdTrigger> mShutdownTrigger; @@ -308,6 +323,7 @@ private: std::mutex mMutex; // for all below size_t mMaxIncomingThreads = 0; + size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads; std::optional<uint32_t> mProtocolVersion; std::condition_variable mAvailableConnectionCv; // for mWaitingThreads diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 08f5eedacd..34f1cbf33a 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 RunRpcServer(AIBinder* service, unsigned int port); bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param), void* param); +// Starts an RPC server on a given port and a given root IBinder object. +// This function sets up the server, calls readyCallback with a given param, and +// then joins before returning. +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 4c2f296135..ab64828220 100644 --- a/libs/binder/include_tls/binder/RpcAuth.h +++ b/libs/binder/include_tls/binder/RpcAuth.h @@ -40,7 +40,7 @@ public: // - 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 cad55fb439..bf2b25b265 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 @@ using android::base::unique_fd; 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 9c04e581a2..ee46fcb884 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -54,6 +54,7 @@ cc_library { defaults: ["libbinder_ndk_host_user"], host_supported: true, + recovery_available: true, llndk: { symbol_file: "libbinder_ndk.map.txt", @@ -155,6 +156,7 @@ cc_library_headers { name: "libbinder_headers_platform_shared", export_include_dirs: ["include_cpp"], vendor_available: true, + recovery_available: true, host_supported: true, // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 49c7b7cc08..8ffa735700 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -555,6 +555,10 @@ pid_t AIBinder_getCallingPid() { return ::android::IPCThreadState::self()->getCallingPid(); } +bool AIBinder_isHandlingTransaction() { + return ::android::IPCThreadState::self()->getServingStackPointer() != nullptr; +} + void AIBinder_incStrong(AIBinder* binder) { if (binder == nullptr) { return; @@ -780,3 +784,7 @@ AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binde AIBinder_incStrong(ndkBinder.get()); return ndkBinder.get(); } + +void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) { + binder->asABBinder()->setMinSchedulerPolicy(policy, priority); +} 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 563d011adc..2b18a0a1e0 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h @@ -543,6 +543,28 @@ binder_status_t AParcel_readStdVectorParcelableElement(const AParcel* parcel, vo } /** + * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'. + */ +template <typename P> +binder_status_t AParcel_writeNullableStdVectorParcelableElement(AParcel* parcel, + const void* vectorData, + size_t index) { + const std::optional<std::vector<P>>* vector = + static_cast<const std::optional<std::vector<P>>*>(vectorData); + return AParcel_writeNullableParcelable(parcel, (*vector)->at(index)); +} + +/** + * Reads a parcelable object of type P inside a std::vector<P> at index 'index' from 'parcel'. + */ +template <typename P> +binder_status_t AParcel_readNullableStdVectorParcelableElement(const AParcel* parcel, + void* vectorData, size_t index) { + std::optional<std::vector<P>>* vector = static_cast<std::optional<std::vector<P>>*>(vectorData); + return AParcel_readNullableParcelable(parcel, &(*vector)->at(index)); +} + +/** * Writes a ScopedFileDescriptor object inside a std::vector<ScopedFileDescriptor> at index 'index' * to 'parcel'. */ @@ -551,11 +573,7 @@ inline binder_status_t AParcel_writeStdVectorParcelableElement<ScopedFileDescrip AParcel* parcel, const void* vectorData, size_t index) { const std::vector<ScopedFileDescriptor>* vector = static_cast<const std::vector<ScopedFileDescriptor>*>(vectorData); - int writeFd = vector->at(index).get(); - if (writeFd < 0) { - return STATUS_UNEXPECTED_NULL; - } - return AParcel_writeParcelFileDescriptor(parcel, writeFd); + return AParcel_writeRequiredParcelFileDescriptor(parcel, vector->at(index)); } /** @@ -567,15 +585,79 @@ inline binder_status_t AParcel_readStdVectorParcelableElement<ScopedFileDescript const AParcel* parcel, void* vectorData, size_t index) { std::vector<ScopedFileDescriptor>* vector = static_cast<std::vector<ScopedFileDescriptor>*>(vectorData); - int readFd; - binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd); - if (status == STATUS_OK) { - if (readFd < 0) { - return STATUS_UNEXPECTED_NULL; - } - vector->at(index).set(readFd); - } - return status; + return AParcel_readRequiredParcelFileDescriptor(parcel, &vector->at(index)); +} + +/** + * Writes a ScopedFileDescriptor object inside a std::optional<std::vector<ScopedFileDescriptor>> at + * index 'index' to 'parcel'. + */ +template <> +inline binder_status_t AParcel_writeNullableStdVectorParcelableElement<ScopedFileDescriptor>( + AParcel* parcel, const void* vectorData, size_t index) { + const std::optional<std::vector<ScopedFileDescriptor>>* vector = + static_cast<const std::optional<std::vector<ScopedFileDescriptor>>*>(vectorData); + return AParcel_writeNullableParcelFileDescriptor(parcel, (*vector)->at(index)); +} + +/** + * Reads a ScopedFileDescriptor object inside a std::optional<std::vector<ScopedFileDescriptor>> at + * index 'index' from 'parcel'. + */ +template <> +inline binder_status_t AParcel_readNullableStdVectorParcelableElement<ScopedFileDescriptor>( + const AParcel* parcel, void* vectorData, size_t index) { + std::optional<std::vector<ScopedFileDescriptor>>* vector = + static_cast<std::optional<std::vector<ScopedFileDescriptor>>*>(vectorData); + return AParcel_readNullableParcelFileDescriptor(parcel, &(*vector)->at(index)); +} + +/** + * Writes an SpAIBinder object inside a std::vector<SpAIBinder> at index 'index' + * to 'parcel'. + */ +template <> +inline binder_status_t AParcel_writeStdVectorParcelableElement<SpAIBinder>(AParcel* parcel, + const void* vectorData, + size_t index) { + const std::vector<SpAIBinder>* vector = static_cast<const std::vector<SpAIBinder>*>(vectorData); + return AParcel_writeRequiredStrongBinder(parcel, vector->at(index)); +} + +/** + * Reads an SpAIBinder object inside a std::vector<SpAIBinder> at index 'index' + * from 'parcel'. + */ +template <> +inline binder_status_t AParcel_readStdVectorParcelableElement<SpAIBinder>(const AParcel* parcel, + void* vectorData, + size_t index) { + std::vector<SpAIBinder>* vector = static_cast<std::vector<SpAIBinder>*>(vectorData); + return AParcel_readRequiredStrongBinder(parcel, &vector->at(index)); +} + +/** + * Writes an SpAIBinder object inside a std::optional<std::vector<SpAIBinder>> at index 'index' + * to 'parcel'. + */ +template <> +inline binder_status_t AParcel_writeNullableStdVectorParcelableElement<SpAIBinder>( + AParcel* parcel, const void* vectorData, size_t index) { + const std::optional<std::vector<SpAIBinder>>* vector = + static_cast<const std::optional<std::vector<SpAIBinder>>*>(vectorData); + return AParcel_writeNullableStrongBinder(parcel, (*vector)->at(index)); +} + +/** + * Reads an SpAIBinder object inside a std::optional<std::vector<SpAIBinder>> at index 'index' + * from 'parcel'. + */ +template <> +inline binder_status_t AParcel_readNullableStdVectorParcelableElement<SpAIBinder>( + const AParcel* parcel, void* vectorData, size_t index) { + std::optional<std::vector<SpAIBinder>>* vector = + static_cast<std::optional<std::vector<SpAIBinder>>*>(vectorData); + return AParcel_readNullableStrongBinder(parcel, &(*vector)->at(index)); } /** @@ -598,6 +680,30 @@ static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vec AParcel_readStdVectorParcelableElement<P>); } +/** + * Convenience API for writing a std::optional<std::vector<P>> + */ +template <typename P> +static inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<P>>& vec) { + if (!vec) return AParcel_writeInt32(parcel, -1); + const void* vectorData = static_cast<const void*>(&vec); + return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()), + AParcel_writeNullableStdVectorParcelableElement<P>); +} + +/** + * Convenience API for reading a std::optional<std::vector<P>> + */ +template <typename P> +static inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<P>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readParcelableArray(parcel, vectorData, + AParcel_nullableStdVectorExternalAllocator<P>, + AParcel_readNullableStdVectorParcelableElement<P>); +} + // @START /** * Writes a vector of int32_t to the next location in a non-null parcel. diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 43533c5277..565542ba55 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -393,6 +393,14 @@ uid_t AIBinder_getCallingUid() __INTRODUCED_IN(29); pid_t AIBinder_getCallingPid() __INTRODUCED_IN(29); /** + * Determine whether the current thread is currently executing an incoming transaction. + * + * \return true if the current thread is currently executing an incoming transaction, and false + * otherwise. + */ +bool AIBinder_isHandlingTransaction() __INTRODUCED_IN(33); + +/** * This can only be called if a strong reference to this object already exists in process. * * Available since API level 29. diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h index e315c798ee..b0217c4efc 100644 --- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h +++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h @@ -55,4 +55,17 @@ __attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requ */ __attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31); +/** + * Sets a minimum scheduler policy for all transactions coming into this + * AIBinder. + * + * This must be called before the object is sent to another process. + * Aborts on invalid values. Not thread safe. + * + * \param binder local server binder to set the policy for + * \param policy scheduler policy as defined in linux UAPI + * \param priority priority. [-20..19] for SCHED_NORMAL, [1..99] for RT + */ +void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) __INTRODUCED_IN(33); + __END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 8605686740..d63a8d0963 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -145,6 +145,8 @@ LIBBINDER_NDK33 { # introduced=33 global: AIBinder_Class_disableInterfaceTokenHeader; AIBinder_DeathRecipient_setOnUnlinked; + AIBinder_isHandlingTransaction; + AIBinder_setMinSchedulerPolicy; # llndk AParcel_marshal; AParcel_unmarshal; }; diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index d9d7cafa7c..ecb044e589 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -17,6 +17,7 @@ rust_library { rustlibs: [ "liblibc", "libbinder_ndk_sys", + "libdowncast_rs", ], host_supported: true, target: { @@ -133,6 +134,7 @@ rust_test { rustlibs: [ "liblibc", "libbinder_ndk_sys", + "libdowncast_rs", ], } diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 41ceee5697..cc5dd06c9b 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -17,12 +17,13 @@ //! 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; use std::borrow::Borrow; use std::cmp::Ordering; +use std::convert::TryFrom; use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::fs::File; @@ -70,6 +71,7 @@ pub trait Interface: Send + Sync { /// An interface can promise to be a stable vendor interface ([`Vintf`]), or /// makes no stability guarantees ([`Local`]). [`Local`] is /// currently the default stability. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Stability { /// Default stability, visible to other modules in the same compilation /// context (e.g. modules on system.img) @@ -85,6 +87,28 @@ impl Default for Stability { } } +impl From<Stability> for i32 { + fn from(stability: Stability) -> i32 { + use Stability::*; + match stability { + Local => 0, + Vintf => 1, + } + } +} + +impl TryFrom<i32> for Stability { + type Error = StatusCode; + fn try_from(stability: i32) -> Result<Stability> { + use Stability::*; + match stability { + 0 => Ok(Local), + 1 => Ok(Vintf), + _ => Err(StatusCode::BAD_VALUE) + } + } +} + /// A local service that can be remotable via Binder. /// /// An object that implement this interface made be made into a Binder service @@ -153,25 +177,25 @@ pub trait IBinderInternal: IBinder { fn get_extension(&mut self) -> Result<Option<SpIBinder>>; /// Create a Parcel that can be used with `submit_transact`. - fn prepare_transact(&self) -> Result<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 @@ -189,8 +213,8 @@ pub trait IBinderInternal: IBinder { 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) } } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 7e8e3a5a5f..81b620e85f 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -113,7 +113,7 @@ pub use crate::binder::{ }; 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}; @@ -127,7 +127,7 @@ pub mod unstable_api { /// The public API usable outside AIDL-generated interface crates. pub mod public_api { - pub use super::parcel::ParcelFileDescriptor; + pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder}; pub use super::{ add_service, force_lazy_services_persist, get_interface, register_lazy_service, wait_for_interface, diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index dad89ec97d..9dba950677 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -23,17 +23,21 @@ use crate::sys; use std::cell::RefCell; use std::convert::TryInto; +use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ptr; use std::fmt; mod file_descriptor; mod parcelable; +mod parcelable_holder; pub use self::file_descriptor::ParcelFileDescriptor; pub use self::parcelable::{ Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, + Parcelable, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG, }; +pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata}; /// Container for a message (data and object references) that can be sent /// through Binder. @@ -49,6 +53,106 @@ pub enum Parcel { 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 } + } + + /// 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 @@ -68,6 +172,21 @@ unsafe impl AsNative<sys::AParcel> for Parcel { } impl Parcel { + /// Create a new empty `Parcel`. + /// + /// Creates a new owned empty parcel that can be written to + /// using the serialization methods and appended to and + /// from using `append_from` and `append_from_all`. + pub fn new() -> Parcel { + let parcel = unsafe { + // Safety: If `AParcel_create` succeeds, it always returns + // a valid pointer. If it fails, the process will crash. + sys::AParcel_create() + }; + assert!(!parcel.is_null()); + Self::Owned(parcel) + } + /// Create a borrowed reference to a parcel object from a raw pointer. /// /// # Safety @@ -77,32 +196,21 @@ impl Parcel { 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 +impl Default for Parcel { + fn default() -> Self { + Self::new() } +} - pub(crate) fn is_owned(&self) -> bool { - match *self { - Self::Owned(_) => true, - Self::Borrowed(_) => false, - } +impl Clone for Parcel { + fn clone(&self) -> Self { + let mut new_parcel = Self::new(); + new_parcel + .append_all_from(self) + .expect("Failed to append from Parcel"); + new_parcel } } @@ -213,6 +321,30 @@ impl Parcel { pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { status_result(sys::AParcel_setDataPosition(self.as_native(), pos)) } + + /// Append a subset of another `Parcel`. + /// + /// This appends `size` bytes of data from `other` starting at offset + /// `start` to the current `Parcel`, or returns an error if not possible. + pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> { + let status = unsafe { + // Safety: `Parcel::appendFrom` from C++ checks that `start` + // and `size` are in bounds, and returns an error otherwise. + // Both `self` and `other` always contain valid pointers. + sys::AParcel_appendFrom( + other.as_native(), + self.as_native_mut(), + start, + size, + ) + }; + status_result(status) + } + + /// Append the contents of another `Parcel`. + pub fn append_all_from(&mut self, other: &Self) -> Result<()> { + self.append_from(other, 0, other.get_data_size()) + } } /// A segment of a writable parcel, used for [`Parcel::sized_write`]. @@ -420,6 +552,18 @@ impl Drop for Parcel { } } +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") @@ -427,43 +571,16 @@ impl fmt::Debug for Parcel { } } -#[cfg(test)] -impl Parcel { - /// Create a new parcel tied to a bogus binder. TESTING ONLY! - /// - /// This can only be used for testing! All real parcel operations must be - /// done in the callback to [`IBinder::transact`] or in - /// [`Remotable::on_transact`] using the parcels provided to these methods. - pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> { - let mut input = ptr::null_mut(); - let status = unsafe { - // Safety: `SpIBinder` guarantees that `binder` always contains a - // valid pointer to an `AIBinder`. We pass a valid, mutable out - // pointer to receive a newly constructed parcel. When successful - // this function assigns a new pointer to an `AParcel` to `input` - // and transfers ownership of this pointer to the caller. Thus, - // after this call, `input` will either be null or point to a valid, - // owned `AParcel`. - sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input) - }; - status_result(status)?; - unsafe { - // Safety: `input` is either null or a valid, owned pointer to an - // `AParcel`, so is valid to safe to - // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel - // pointer. - Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL) - } +impl fmt::Debug for OwnedParcel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedParcel") + .finish() } } #[test] fn test_read_write() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA)); @@ -493,11 +610,7 @@ fn test_read_write() { #[test] #[allow(clippy::float_cmp)] fn test_read_data() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let str_start = parcel.get_data_position(); parcel.write(&b"Hello, Binder!\0"[..]).unwrap(); @@ -572,11 +685,7 @@ fn test_read_data() { #[test] fn test_utf8_utf16_conversions() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(parcel.write("Hello, Binder!").is_ok()); @@ -636,11 +745,7 @@ fn test_utf8_utf16_conversions() { #[test] fn test_sized_write() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); let arr = [1i32, 2i32, 3i32]; @@ -668,3 +773,43 @@ fn test_sized_write() { &arr, ); } + +#[test] +fn test_append_from() { + let mut parcel1 = Parcel::new(); + parcel1.write(&42i32).expect("Could not perform write"); + + let mut parcel2 = Parcel::new(); + assert_eq!(Ok(()), parcel2.append_all_from(&parcel1)); + assert_eq!(4, parcel2.get_data_size()); + assert_eq!(Ok(()), parcel2.append_all_from(&parcel1)); + assert_eq!(8, parcel2.get_data_size()); + unsafe { + parcel2.set_data_position(0).unwrap(); + } + assert_eq!(Ok(42), parcel2.read::<i32>()); + assert_eq!(Ok(42), parcel2.read::<i32>()); + + let mut parcel2 = Parcel::new(); + assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); + assert_eq!(Ok(()), parcel2.append_from(&parcel1, 2, 2)); + assert_eq!(4, parcel2.get_data_size()); + unsafe { + parcel2.set_data_position(0).unwrap(); + } + assert_eq!(Ok(42), parcel2.read::<i32>()); + + let mut parcel2 = Parcel::new(); + assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); + assert_eq!(2, parcel2.get_data_size()); + unsafe { + parcel2.set_data_position(0).unwrap(); + } + assert_eq!(Err(StatusCode::NOT_ENOUGH_DATA), parcel2.read::<i32>()); + + let mut parcel2 = Parcel::new(); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 4, 2)); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, 4)); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, -1, 4)); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, -1)); +} diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index f71a686959..8bcc5d0092 100644 --- a/libs/binder/rust/src/parcel/file_descriptor.rs +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -94,8 +94,6 @@ impl SerializeOption for ParcelFileDescriptor { } } -impl SerializeArray for Option<ParcelFileDescriptor> {} - impl DeserializeOption for ParcelFileDescriptor { fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { let mut fd = -1i32; @@ -126,8 +124,6 @@ impl DeserializeOption for ParcelFileDescriptor { } } -impl DeserializeArray for Option<ParcelFileDescriptor> {} - impl Deserialize for ParcelFileDescriptor { fn deserialize(parcel: &Parcel) -> Result<Self> { Deserialize::deserialize(parcel) diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index 56c61651cf..ec00e1dc86 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -14,19 +14,42 @@ * limitations under the License. */ -use crate::binder::{AsNative, FromIBinder, Strong}; +use crate::binder::{AsNative, FromIBinder, Stability, Strong}; use crate::error::{status_result, status_t, Result, Status, StatusCode}; use crate::parcel::Parcel; use crate::proxy::SpIBinder; use crate::sys; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; use std::os::raw::{c_char, c_ulong}; use std::mem::{self, MaybeUninit}; use std::ptr; use std::slice; +/// Super-trait for Binder parcelables. +/// +/// This trait is equivalent `android::Parcelable` in C++, +/// and defines a common interface that all parcelables need +/// to implement. +pub trait Parcelable { + /// Internal serialization function for parcelables. + /// + /// This method is mainly for internal use. + /// `Serialize::serialize` and its variants are generally + /// preferred over this function, since the former also + /// prepend a header. + fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>; + + /// Internal deserialization function for parcelables. + /// + /// This method is mainly for internal use. + /// `Deserialize::deserialize` and its variants are generally + /// preferred over this function, since the former also + /// parse the additional header. + fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>; +} + /// A struct whose instances can be written to a [`Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Serialize { @@ -162,6 +185,18 @@ unsafe extern "C" fn deserialize_element<T: Deserialize>( StatusCode::OK as status_t } +/// Flag that specifies that the following parcelable is present. +/// +/// This is the Rust equivalent of `Parcel::kNonNullParcelableFlag` +/// from `include/binder/Parcel.h` in C++. +pub const NON_NULL_PARCELABLE_FLAG: i32 = 1; + +/// Flag that specifies that the following parcelable is absent. +/// +/// This is the Rust equivalent of `Parcel::kNullParcelableFlag` +/// from `include/binder/Parcel.h` in C++. +pub const NULL_PARCELABLE_FLAG: i32 = 0; + /// Helper trait for types that can be nullable when serialized. // We really need this trait instead of implementing `Serialize for Option<T>` // because of the Rust orphan rule which prevents us from doing @@ -173,10 +208,10 @@ pub trait SerializeOption: Serialize { /// Serialize an Option of this type into the given [`Parcel`]. fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { if let Some(inner) = this { - parcel.write(&1i32)?; + parcel.write(&NON_NULL_PARCELABLE_FLAG)?; parcel.write(inner) } else { - parcel.write(&0i32) + parcel.write(&NULL_PARCELABLE_FLAG) } } } @@ -186,7 +221,7 @@ pub trait DeserializeOption: Deserialize { /// Deserialize an Option of this type from the given [`Parcel`]. fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { let null: i32 = parcel.read()?; - if null == 0 { + if null == NULL_PARCELABLE_FLAG { Ok(None) } else { parcel.read().map(Some) @@ -348,6 +383,9 @@ macro_rules! impl_parcelable { }; } +impl<T: DeserializeOption> DeserializeArray for Option<T> {} +impl<T: SerializeOption> SerializeArray for Option<T> {} + parcelable_primitives! { impl Serialize for bool = sys::AParcel_writeBool; impl Deserialize for bool = sys::AParcel_readBool; @@ -502,8 +540,6 @@ impl SerializeOption for str { } } -impl SerializeArray for Option<&str> {} - impl Serialize for str { fn serialize(&self, parcel: &mut Parcel) -> Result<()> { Some(self).serialize(parcel) @@ -526,8 +562,6 @@ impl SerializeOption for String { } } -impl SerializeArray for Option<String> {} - impl Deserialize for Option<String> { fn deserialize(parcel: &Parcel) -> Result<Self> { let mut vec: Option<Vec<u8>> = None; @@ -608,6 +642,18 @@ impl<T: DeserializeArray> DeserializeOption for Vec<T> { } } +impl Serialize for Stability { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + i32::from(*self).serialize(parcel) + } +} + +impl Deserialize for Stability { + fn deserialize(parcel: &Parcel) -> Result<Self> { + i32::deserialize(parcel).and_then(Stability::try_from) + } +} + impl Serialize for Status { fn serialize(&self, parcel: &mut Parcel) -> Result<()> { unsafe { @@ -699,19 +745,53 @@ impl<T: DeserializeOption> Deserialize for Option<T> { } } +/// Implement `Serialize` trait and friends for a parcelable +/// +/// This is an internal macro used by the AIDL compiler to implement +/// `Serialize`, `SerializeArray` and `SerializeOption` for +/// structured parcelables. The target type must implement the +/// `Parcelable` trait. +/// ``` +#[macro_export] +macro_rules! impl_serialize_for_parcelable { + ($parcelable:ident) => { + impl $crate::parcel::Serialize for $parcelable { + fn serialize( + &self, + parcel: &mut $crate::parcel::Parcel, + ) -> $crate::Result<()> { + <Self as $crate::parcel::SerializeOption>::serialize_option( + Some(self), + parcel, + ) + } + } + + impl $crate::parcel::SerializeArray for $parcelable {} + + impl $crate::parcel::SerializeOption for $parcelable { + fn serialize_option( + this: Option<&Self>, + parcel: &mut $crate::parcel::Parcel, + ) -> $crate::Result<()> { + if let Some(this) = this { + use $crate::parcel::Parcelable; + parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?; + this.write_to_parcel(parcel) + } else { + parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG) + } + } + } + } +} + /// Implement `Deserialize` trait and friends for a parcelable /// /// This is an internal macro used by the AIDL compiler to implement /// `Deserialize`, `DeserializeArray` and `DeserializeOption` for -/// structured parcelables. The target type must implement a -/// `deserialize_parcelable` method with the following signature: -/// ```no_run -/// fn deserialize_parcelable( -/// &mut self, -/// parcel: &binder::parcel::Parcelable, -/// ) -> binder::Result<()> { -/// // ... -/// } +/// structured parcelables. The target type must implement the +/// `Parcelable` trait. /// ``` #[macro_export] macro_rules! impl_deserialize_for_parcelable { @@ -729,10 +809,11 @@ macro_rules! impl_deserialize_for_parcelable { parcel: &$crate::parcel::Parcel, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; - if status == 0 { + if status == $crate::parcel::NULL_PARCELABLE_FLAG { Err($crate::StatusCode::UNEXPECTED_NULL) } else { - self.deserialize_parcelable(parcel) + use $crate::parcel::Parcelable; + self.read_from_parcel(parcel) } } } @@ -752,12 +833,13 @@ macro_rules! impl_deserialize_for_parcelable { parcel: &$crate::parcel::Parcel, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; - if status == 0 { + if status == $crate::parcel::NULL_PARCELABLE_FLAG { *this = None; Ok(()) } else { + use $crate::parcel::Parcelable; this.get_or_insert_with(Self::default) - .deserialize_parcelable(parcel) + .read_from_parcel(parcel) } } } @@ -790,10 +872,6 @@ impl<T: DeserializeOption> DeserializeOption for Box<T> { #[test] fn test_custom_parcelable() { - use crate::binder::Interface; - use crate::native::Binder; - let mut service = Binder::new(()).as_binder(); - struct Custom(u32, bool, String, Vec<String>); impl Serialize for Custom { @@ -826,7 +904,7 @@ fn test_custom_parcelable() { let custom = Custom(123_456_789, true, string8, strs); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(custom.serialize(&mut parcel).is_ok()); @@ -846,13 +924,9 @@ fn test_custom_parcelable() { #[test] #[allow(clippy::excessive_precision)] fn test_slice_parcelables() { - use crate::binder::Interface; - use crate::native::Binder; - let mut service = Binder::new(()).as_binder(); - let bools = [true, false, false, true]; - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(bools.serialize(&mut parcel).is_ok()); @@ -876,7 +950,7 @@ fn test_slice_parcelables() { let u8s = [101u8, 255, 42, 117]; - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(parcel.write(&u8s[..]).is_ok()); diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs new file mode 100644 index 0000000000..bccfd2d25e --- /dev/null +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -0,0 +1,261 @@ +/* + * 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 crate::binder::Stability; +use crate::error::{Result, StatusCode}; +use crate::parcel::{OwnedParcel, Parcel, Parcelable}; +use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable}; + +use downcast_rs::{impl_downcast, DowncastSync}; +use std::any::Any; +use std::sync::{Arc, Mutex}; + +/// Metadata that `ParcelableHolder` needs for all parcelables. +/// +/// The compiler auto-generates implementations of this trait +/// for AIDL parcelables. +pub trait ParcelableMetadata { + /// The Binder parcelable descriptor string. + /// + /// This string is a unique identifier for a Binder parcelable. + fn get_descriptor() -> &'static str; + + /// The Binder parcelable stability. + fn get_stability(&self) -> Stability { + Stability::Local + } +} + +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: Arc<dyn AnyParcelable>, + name: String, + }, + Parcel(OwnedParcel), +} + +impl Default for ParcelableHolderData { + fn default() -> Self { + ParcelableHolderData::Empty + } +} + +/// A container that can hold any arbitrary `Parcelable`. +/// +/// This type is currently used for AIDL parcelable fields. +/// +/// `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)] +pub struct ParcelableHolder { + // 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 `Mutex` here for a performance + // improvement, but then callers would require a mutable + // `ParcelableHolder` even for that getter method. + data: Mutex<ParcelableHolderData>, + stability: Stability, +} + +impl ParcelableHolder { + /// Construct a new `ParcelableHolder` with the given stability. + pub fn new(stability: Stability) -> Self { + Self { + data: Mutex::new(ParcelableHolderData::Empty), + stability, + } + } + + /// Reset the contents of this `ParcelableHolder`. + /// + /// Note that this method does not reset the stability, + /// only the contents. + pub fn reset(&mut self) { + *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: Arc<T>) -> Result<()> + where + T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync, + { + if self.stability > p.get_stability() { + return Err(StatusCode::BAD_VALUE); + } + + *self.data.get_mut().unwrap() = ParcelableHolderData::Parcelable { + parcelable: p, + name: T::get_descriptor().into(), + }; + + Ok(()) + } + + /// Retrieve the parcelable stored in this `ParcelableHolder`. + /// + /// This method attempts to retrieve the parcelable inside + /// the current object as a parcelable of type `T`. + /// The object is validated against `T` by checking that + /// its parcelable descriptor matches the one returned + /// by `T::get_descriptor()`. + /// + /// Returns one of the following: + /// * `Err(_)` in case of error + /// * `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<Arc<T>>> + where + T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync, + { + let parcelable_desc = T::get_descriptor(); + let mut data = self.data.lock().unwrap(); + match *data { + ParcelableHolderData::Empty => Ok(None), + ParcelableHolderData::Parcelable { + ref parcelable, + ref name, + } => { + if name != parcelable_desc { + return Err(StatusCode::BAD_VALUE); + } + + match Arc::clone(parcelable).downcast_arc::<T>() { + Err(_) => Err(StatusCode::BAD_VALUE), + Ok(x) => Ok(Some(x)), + } + } + ParcelableHolderData::Parcel(ref mut parcel) => { + let parcel = parcel.borrowed(); + unsafe { + // Safety: 0 should always be a valid position. + parcel.set_data_position(0)?; + } + + let name: String = parcel.read()?; + if name != parcelable_desc { + return Ok(None); + } + + let mut parcelable = T::default(); + parcelable.read_from_parcel(&parcel)?; + + let parcelable = Arc::new(parcelable); + let result = Arc::clone(&parcelable); + *data = ParcelableHolderData::Parcelable { parcelable, name }; + + Ok(Some(result)) + } + } + } + + /// Return the stability value of this object. + pub fn get_stability(&self) -> Stability { + self.stability + } +} + +impl_serialize_for_parcelable!(ParcelableHolder); +impl_deserialize_for_parcelable!(ParcelableHolder); + +impl Parcelable for ParcelableHolder { + fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> { + parcel.write(&self.stability)?; + + let mut data = self.data.lock().unwrap(); + match *data { + ParcelableHolderData::Empty => parcel.write(&0i32), + ParcelableHolderData::Parcelable { + ref parcelable, + ref name, + } => { + let length_start = parcel.get_data_position(); + parcel.write(&0i32)?; + + let data_start = parcel.get_data_position(); + parcel.write(name)?; + parcelable.write_to_parcel(parcel)?; + + let end = parcel.get_data_position(); + unsafe { + // Safety: we got the position from `get_data_position`. + parcel.set_data_position(length_start)?; + } + + assert!(end >= data_start); + parcel.write(&(end - data_start))?; + unsafe { + // Safety: we got the position from `get_data_position`. + parcel.set_data_position(end)?; + } + + Ok(()) + } + ParcelableHolderData::Parcel(ref mut p) => { + let p = p.borrowed(); + parcel.write(&p.get_data_size())?; + parcel.append_all_from(&p) + } + } + } + + fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> { + self.stability = parcel.read()?; + + let data_size: i32 = parcel.read()?; + if data_size < 0 { + // C++ returns BAD_VALUE here, + // while Java returns ILLEGAL_ARGUMENT + return Err(StatusCode::BAD_VALUE); + } + if data_size == 0 { + *self.data.get_mut().unwrap() = ParcelableHolderData::Empty; + return Ok(()); + } + + // TODO: C++ ParcelableHolder accepts sizes up to SIZE_MAX here, but we + // only go up to i32::MAX because that's what our API uses everywhere + let data_start = parcel.get_data_position(); + let data_end = data_start + .checked_add(data_size) + .ok_or(StatusCode::BAD_VALUE)?; + + let mut new_parcel = OwnedParcel::new(); + new_parcel + .borrowed() + .append_from(parcel, data_start, data_size)?; + *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel); + + unsafe { + // Safety: `append_from` checks if `data_size` overflows + // `parcel` and returns `BAD_VALUE` if that happens. We also + // explicitly check for negative and zero `data_size` above, + // so `data_end` is guaranteed to be greater than `data_start`. + parcel.set_data_position(data_end)?; + } + + Ok(()) + } +} diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 68fa34bb4a..a8d0c33034 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -22,7 +22,7 @@ use crate::binder::{ }; 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 Drop for SpIBinder { } 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 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { 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 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { // 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) } } @@ -429,7 +427,6 @@ impl SerializeOption for SpIBinder { } impl SerializeArray for SpIBinder {} -impl SerializeArray for Option<&SpIBinder> {} impl Deserialize for SpIBinder { fn deserialize(parcel: &Parcel) -> Result<SpIBinder> { @@ -447,7 +444,6 @@ impl DeserializeOption for SpIBinder { } impl DeserializeArray for SpIBinder {} -impl DeserializeArray for Option<SpIBinder> {} /// A weak reference to a Binder remote object. /// diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index 0e05f10dfe..0aef744f98 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -99,6 +99,17 @@ impl ThreadState { } } + /// Determine whether the current thread is currently executing an incoming transaction. + /// + /// \return true if the current thread is currently executing an incoming transaction, and false + /// otherwise. + pub fn is_handling_transaction() -> bool { + unsafe { + // Safety: Safe FFI + sys::AIBinder_isHandlingTransaction() + } + } + /// This function makes the client's security context available to the /// service calling this function. This can be used for access control. /// It does not suffer from the TOCTOU issues of get_calling_pid. diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp index 23e34aa8ef..777f3c9354 100644 --- a/libs/binder/servicedispatcher.cpp +++ b/libs/binder/servicedispatcher.cpp @@ -90,7 +90,6 @@ int Dispatch(const char* name, const ServiceRetriever& serviceRetriever) { 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 @@ int wrapServiceManager(const ServiceRetriever& serviceRetriever) { 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/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl index 9e1078870c..fdd02a4435 100644 --- a/libs/binder/tests/IBinderRpcTest.aidl +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -18,6 +18,9 @@ interface IBinderRpcTest { oneway void sendString(@utf8InCpp String str); @utf8InCpp String doubleString(@utf8InCpp String str); + // get the port that a client used to connect to this object + int getClientPort(); + // number of known RPC binders to process, RpcState::countBinders by session int[] countBinders(); diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp index eec3b447de..464da60dde 100644 --- a/libs/binder/tests/binderHostDeviceTest.cpp +++ b/libs/binder/tests/binderHostDeviceTest.cpp @@ -65,7 +65,9 @@ MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected void initHostRpcServiceManagerOnce() { static std::once_flag gSmOnce; - std::call_once(gSmOnce, [] { setDefaultServiceManager(createRpcDelegateServiceManager()); }); + std::call_once(gSmOnce, [] { + setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1})); + }); } // Test for host service manager. diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 639876ffe3..4d316f72f0 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -436,6 +436,11 @@ class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestE }; }; +TEST_F(BinderLibTest, CannotUseBinderAfterFork) { + // EXPECT_DEATH works by forking the process + EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork"); +} + TEST_F(BinderLibTest, WasParceled) { auto binder = sp<BBinder>::make(); EXPECT_FALSE(binder->wasParceled()); @@ -1197,7 +1202,6 @@ public: 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); diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp index f8718aad1e..52ba9b00fd 100644 --- a/libs/binder/tests/binderRpcBenchmark.cpp +++ b/libs/binder/tests/binderRpcBenchmark.cpp @@ -206,7 +206,6 @@ void forkRpcServer(const char* addr, const sp<RpcServer>& server) { 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 2011801f27..55ad3c6975 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -109,7 +109,6 @@ TEST_P(BinderRpcSimple, SetExternalServerTest) { 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()); @@ -174,6 +173,7 @@ public: class MyBinderRpcTest : public BnBinderRpcTest { public: wp<RpcServer> server; + int port = 0; Status sendString(const std::string& str) override { (void)str; @@ -183,6 +183,10 @@ public: *strstr = str + str; return Status::ok(); } + Status getClientPort(int* out) override { + *out = port; + return Status::ok(); + } Status countBinders(std::vector<int32_t>* out) override { sp<RpcServer> spServer = server.promote(); if (spServer == nullptr) { @@ -481,6 +485,7 @@ public: size_t numThreads = 1; size_t numSessions = 1; size_t numIncomingConnections = 0; + size_t numOutgoingConnections = SIZE_MAX; }; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { @@ -537,7 +542,6 @@ public: auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); - server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); server->setMaxThreads(options.numThreads); unsigned int outPort = 0; @@ -614,6 +618,7 @@ public: for (const auto& session : sessions) { session->setMaxIncomingThreads(options.numIncomingConnections); + session->setMaxOutgoingThreads(options.numOutgoingConnections); switch (socketType) { case SocketType::PRECONNECTED: @@ -641,13 +646,41 @@ public: BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) { BinderRpcTestProcessSession ret{ - .proc = createRpcTestSocketServerProcess(options, - [&](const sp<RpcServer>& server) { - sp<MyBinderRpcTest> service = - new MyBinderRpcTest; - server->setRootObject(service); - service->server = server; - }), + .proc = createRpcTestSocketServerProcess( + options, + [&](const sp<RpcServer>& server) { + server->setPerSessionRootObject([&](const sockaddr* addr, + socklen_t len) { + sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make(); + switch (addr->sa_family) { + case AF_UNIX: + // nothing to save + break; + case AF_VSOCK: + CHECK_EQ(len, sizeof(sockaddr_vm)); + service->port = reinterpret_cast<const sockaddr_vm*>(addr) + ->svm_port; + break; + case AF_INET: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = + ntohs(reinterpret_cast<const sockaddr_in*>(addr) + ->sin_port); + break; + case AF_INET6: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = + ntohs(reinterpret_cast<const sockaddr_in6*>(addr) + ->sin6_port); + break; + default: + LOG_ALWAYS_FATAL("Unrecognized address family %d", + addr->sa_family); + } + service->server = server; + return service; + }); + }), }; ret.rootBinder = ret.proc.sessions.at(0).root; @@ -655,6 +688,9 @@ public: return ret; } + + void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, + size_t sleepMs = 500); }; TEST_P(BinderRpc, Ping) { @@ -677,6 +713,27 @@ TEST_P(BinderRpc, MultipleSessions) { } } +TEST_P(BinderRpc, SeparateRootObject) { + SocketType type = std::get<0>(GetParam()); + if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) { + // we can't get port numbers for unix sockets + return; + } + + auto proc = createRpcTestSocketServerProcess({.numSessions = 2}); + + int port1 = 0; + EXPECT_OK(proc.rootIface->getClientPort(&port1)); + + sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root); + int port2; + EXPECT_OK(rootIface2->getClientPort(&port2)); + + // we should have a different IBinderRpcTest object created for each + // session, because we use setPerSessionRootObject + EXPECT_NE(port1, port2); +} + TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) { auto proc = createRpcTestSocketServerProcess({}); Parcel data; @@ -685,13 +742,21 @@ TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) { } TEST_P(BinderRpc, AppendSeparateFormats) { - auto proc = createRpcTestSocketServerProcess({}); + auto proc1 = createRpcTestSocketServerProcess({}); + auto proc2 = createRpcTestSocketServerProcess({}); + + Parcel pRaw; Parcel p1; - p1.markForBinder(proc.rootBinder); + p1.markForBinder(proc1.rootBinder); p1.writeInt32(3); + EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, p1.dataSize())); + EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize())); + Parcel p2; + p2.markForBinder(proc2.rootBinder); + p2.writeInt32(7); EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize())); EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize())); @@ -996,28 +1061,39 @@ TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { for (auto& t : ts) t.join(); } -TEST_P(BinderRpc, ThreadPoolOverSaturated) { - constexpr size_t kNumThreads = 10; - constexpr size_t kNumCalls = kNumThreads + 3; - constexpr size_t kSleepMs = 500; - - auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); - +void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, + size_t sleepMs) { size_t epochMsBefore = epochMillis(); std::vector<std::thread> ts; - for (size_t i = 0; i < kNumCalls; i++) { - ts.push_back(std::thread([&] { proc.rootIface->sleepMs(kSleepMs); })); + for (size_t i = 0; i < numCalls; i++) { + ts.push_back(std::thread([&] { iface->sleepMs(sleepMs); })); } for (auto& t : ts) t.join(); size_t epochMsAfter = epochMillis(); - EXPECT_GE(epochMsAfter, epochMsBefore + 2 * kSleepMs); + EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs); // Potential flake, but make sure calls are handled in parallel. - EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs); + EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs); +} + +TEST_P(BinderRpc, ThreadPoolOverSaturated) { + constexpr size_t kNumThreads = 10; + constexpr size_t kNumCalls = kNumThreads + 3; + auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); + testThreadPoolOverSaturated(proc.rootIface, kNumCalls); +} + +TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { + constexpr size_t kNumThreads = 20; + constexpr size_t kNumOutgoingConnections = 10; + constexpr size_t kNumCalls = kNumOutgoingConnections + 3; + auto proc = createRpcTestSocketServerProcess( + {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections}); + testThreadPoolOverSaturated(proc.rootIface, kNumCalls); } TEST_P(BinderRpc, ThreadingStressTest) { @@ -1227,11 +1303,20 @@ TEST_P(BinderRpc, Die) { } 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()); @@ -1283,11 +1368,20 @@ TEST_P(BinderRpc, Fds) { ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?"); } +TEST_P(BinderRpc, AidlDelegatorTest) { + auto proc = createRpcTestSocketServerProcess({}); + auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface); + ASSERT_NE(nullptr, myDelegator); + + std::string doubled; + EXPECT_OK(myDelegator->doubleString("cool ", &doubled)); + EXPECT_EQ("cool cool ", doubled); +} + static bool testSupportVsockLoopback() { // 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; @@ -1376,7 +1470,6 @@ private: 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>(); @@ -1416,7 +1509,6 @@ TEST(BinderRpc, Java) { 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(); @@ -1455,7 +1547,6 @@ public: 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"; @@ -1917,5 +2008,6 @@ INSTANTIATE_TEST_CASE_P( 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 f1fe164c76..155a25bbcd 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -284,7 +284,6 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { FUZZ_LOG() << "readObject: " << obj; }, PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid), - PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize), PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize), // additional parcelable objects defined in libbinder @@ -301,6 +300,23 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { FUZZ_LOG() << "ParcelableHolder status: " << status; }, PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable), + [] (const ::android::Parcel& p, uint8_t /* data */) { + FUZZ_LOG() << "about to call hasFileDescriptorsInRange() with status"; + size_t offset = p.readUint32(); + size_t length = p.readUint32(); + bool result; + 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 6b783a4804..c0a762d1de 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -95,6 +95,11 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector), PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<SomeParcelable>, ndk::AParcel_readVector), + PARCEL_READ(std::optional<std::vector<std::optional<SomeParcelable>>>, ndk::AParcel_readVector), + PARCEL_READ(std::vector<ndk::SpAIBinder>, ndk::AParcel_readVector), + 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<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/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp index c0f0a12121..71e847fd1e 100644 --- a/libs/binder/tests/rpc_fuzzer/Android.bp +++ b/libs/binder/tests/rpc_fuzzer/Android.bp @@ -14,6 +14,7 @@ cc_fuzz { fuzz_config: { cc: ["smoreland@google.com"], }, + corpus: ["corpus/*"], dictionary: "binder_rpc_fuzzer.dict", srcs: [ diff --git a/libs/binder/tests/rpc_fuzzer/corpus/special_transaction b/libs/binder/tests/rpc_fuzzer/corpus/special_transaction Binary files differnew file mode 100644 index 0000000000..37228ee8cd --- /dev/null +++ b/libs/binder/tests/rpc_fuzzer/corpus/special_transaction diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp index 518849a3e4..a8713a24d6 100644 --- a/libs/binder/tests/rpc_fuzzer/main.cpp +++ b/libs/binder/tests/rpc_fuzzer/main.cpp @@ -119,7 +119,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 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(); }); @@ -158,6 +157,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { } } + usleep(10000); + if (hangupBeforeShutdown) { connections.clear(); while (!server->listSessions().empty() || server->numUninitializedSessions()) { diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp index 20c55699cc..e77c55c669 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp @@ -44,7 +44,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 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 64203f78a8..319003800c 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -146,7 +146,6 @@ cc_library_shared { ], shared_libs: [ - "android.frameworks.bufferhub@1.0", "libbinder", "libbufferhub", "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. @@ -175,7 +174,6 @@ cc_library_shared { "BufferHubProducer.cpp", ], exclude_shared_libs: [ - "android.frameworks.bufferhub@1.0", "libbufferhub", "libbufferhubqueue", "libinput", diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 30d19e3af4..b3647d6126 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -301,7 +301,7 @@ sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer( kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, + DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, "[GLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 2f31888bf6..6882ea3090 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -191,7 +191,7 @@ sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() { // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, + DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h index e3d705fa35..abcd527966 100644 --- a/libs/permission/include/binder/AppOpsManager.h +++ b/libs/permission/include/binder/AppOpsManager.h @@ -147,7 +147,8 @@ public: OP_ACTIVITY_RECOGNITION_SOURCE = 113, OP_BLUETOOTH_ADVERTISE = 114, OP_RECORD_INCOMING_PHONE_AUDIO = 115, - _NUM_OP = 116 + OP_NEARBY_WIFI_DEVICES = 116, + _NUM_OP = 117 }; AppOpsManager(); diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index ae8f2384c4..cc627b8bc8 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -388,7 +388,10 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { drawBlurLayers(renderengine, display, dstTexture); } - // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; + // The majority of skia shaders needed by RenderEngine are related to sampling images. + // These need to be generated with various source textures. + // Make a list of applicable sources. + // GRALLOC_USAGE_HW_TEXTURE should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE. const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; sp<GraphicBuffer> externalBuffer = new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, @@ -396,24 +399,24 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { const auto externalTexture = std::make_shared<ExternalTexture>(externalBuffer, *renderengine, ExternalTexture::Usage::READABLE); + std::vector<const std::shared_ptr<ExternalTexture>> textures = + {srcTexture, externalTexture}; - // Another external texture with a different pixel format triggers useIsOpaqueWorkaround + // Another external texture with a different pixel format triggers useIsOpaqueWorkaround. + // It doesn't have to be f16, but it can't be the usual 8888. sp<GraphicBuffer> f16ExternalBuffer = new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_FP16, 1, usageExternal, "primeShaderCache_external_f16"); - const auto f16ExternalTexture = + // The F16 texture may not be usable on all devices, so check first that it was created. + status_t error = f16ExternalBuffer->initCheck(); + if (!error) { + const auto f16ExternalTexture = std::make_shared<ExternalTexture>(f16ExternalBuffer, *renderengine, ExternalTexture::Usage::READABLE); + textures.push_back(f16ExternalTexture); + } - // The majority of shaders are related to sampling images. - // These need to be generated with various source textures - // The F16 texture may not be usable on all devices, so check first that it was created with - // the requested usage bit. - auto textures = {srcTexture, externalTexture}; - auto texturesWithF16 = {srcTexture, externalTexture, f16ExternalTexture}; - bool canUsef16 = f16ExternalBuffer->getUsage() & GRALLOC_USAGE_HW_TEXTURE; - - for (auto texture : canUsef16 ? texturesWithF16 : textures) { + for (auto texture : textures) { drawImageLayers(renderengine, display, dstTexture, texture); // Draw layers for b/185569240. drawClippedLayers(renderengine, display, dstTexture, texture); diff --git a/libs/vibrator/OWNERS b/libs/vibrator/OWNERS index 0997e9f377..d073e2bd46 100644 --- a/libs/vibrator/OWNERS +++ b/libs/vibrator/OWNERS @@ -1,2 +1 @@ -lsandrade@google.com -michaelwr@google.com +include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index bba6e22354..1be5a96f67 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -77,7 +77,6 @@ cc_library_shared { "libsensor", "libsensorprivacy", "libpermission", - "packagemanager_aidl-cpp", ], } diff --git a/services/vibratorservice/OWNERS b/services/vibratorservice/OWNERS new file mode 100644 index 0000000000..d073e2bd46 --- /dev/null +++ b/services/vibratorservice/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc index 0de0f9eec7..1612743867 100644 --- a/services/vr/virtual_touchpad/virtual_touchpad.rc +++ b/services/vr/virtual_touchpad/virtual_touchpad.rc @@ -2,4 +2,4 @@ service virtual_touchpad /system/bin/virtual_touchpad class core user system group system input uhid - writepid /dev/cpuset/system/tasks + task_profiles VrServiceCapacityNormal |