Merge "installd: Skip profile preparation if primary.prof does not exist"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 8bcb1e5..7ef26bf 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,10 +1,12 @@
[Builtin Hooks]
+bpfmt = true
clang_format = true
[Builtin Hooks Options]
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/idlcli/
+ cmds/installd/
cmds/servicemanager/
include/input/
include/powermanager/
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 783a475..6459443 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -239,7 +239,7 @@
} },
{ "memory", "Memory", 0, {
{ OPT, "events/mm_event/mm_event_record/enable" },
- { OPT, "events/kmem/rss_stat/enable" },
+ { OPT, "events/synthetic/rss_stat_throttled/enable" },
{ OPT, "events/kmem/ion_heap_grow/enable" },
{ OPT, "events/kmem/ion_heap_shrink/enable" },
{ OPT, "events/ion/ion_stat/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 01c4723..34ccb21 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -282,6 +282,21 @@
chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu23/trace
chmod 0666 /sys/kernel/tracing/per_cpu/cpu23/trace
+# Setup synthetic events
+ chmod 0666 /sys/kernel/tracing/synthetic_events
+ chmod 0666 /sys/kernel/debug/tracing/synthetic_events
+
+ # rss_stat_throttled
+ write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
+ write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
+
+# Set up histogram triggers
+ # rss_stat_throttled (bucket size == 512KB)
+ chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger
+ chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
+ write /sys/kernel/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)"
+ write /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)"
+
# Only create the tracing instance if persist.mm_events.enabled
# Attempting to remove the tracing instance after it has been created
# will likely fail with EBUSY as it would be in use by traced_probes.
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp
index e29ff4c..27690a3 100644
--- a/cmds/installd/CacheItem.cpp
+++ b/cmds/installd/CacheItem.cpp
@@ -116,6 +116,7 @@
break;
}
}
+ fts_close(fts);
} else {
if (tombstone) {
if (truncate(path.c_str(), 0) != 0) {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 157d259..9ee1cc1 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -422,9 +422,131 @@
return true;
}
+static bool chown_app_dir(const std::string& path, uid_t uid, uid_t previousUid, gid_t cacheGid) {
+ FTS* fts;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
+ return false;
+ }
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ if (p->fts_info == FTS_D && p->fts_level == 1
+ && (strcmp(p->fts_name, "cache") == 0
+ || strcmp(p->fts_name, "code_cache") == 0)) {
+ // Mark cache dirs
+ p->fts_number = 1;
+ } else {
+ // Inherit parent's number
+ p->fts_number = p->fts_parent->fts_number;
+ }
+
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (p->fts_statp->st_uid == previousUid) {
+ if (lchown(p->fts_path, uid, p->fts_number ? cacheGid : uid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ } else {
+ LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected UID "
+ << p->fts_statp->st_uid << " instead of " << previousUid;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ return true;
+}
+
+static void chown_app_profile_dir(const std::string &packageName, int32_t appId, int32_t userId) {
+ uid_t uid = multiuser_get_uid(userId, appId);
+ gid_t sharedGid = multiuser_get_shared_gid(userId, appId);
+
+ const std::string profile_dir =
+ create_primary_current_profile_package_dir_path(userId, packageName);
+ char *argv[] = { (char*) profile_dir.c_str(), nullptr };
+ if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) {
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, uid, uid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ }
+
+ const std::string ref_profile_path =
+ create_primary_reference_profile_package_dir_path(packageName);
+ argv[0] = (char *) ref_profile_path.c_str();
+ if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) {
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ if (p->fts_info == FTS_D && p->fts_level == 0) {
+ if (chown(p->fts_path, AID_SYSTEM, sharedGid) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ continue;
+ }
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, sharedGid, sharedGid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ }
+}
+
+static binder::Status createAppDataDirs(const std::string& path,
+ int32_t uid, int32_t* previousUid, int32_t cacheGid,
+ const std::string& seInfo, mode_t targetMode) {
+ struct stat st{};
+ bool existing = (stat(path.c_str(), &st) == 0);
+ if (existing) {
+ if (*previousUid < 0) {
+ // If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner
+ // of the directory as previousUid. This is required because it is not always possible
+ // to chown app data during app upgrade (e.g. secondary users' CE storage not unlocked)
+ *previousUid = st.st_uid;
+ }
+ if (*previousUid != uid) {
+ if (!chown_app_dir(path, uid, *previousUid, cacheGid)) {
+ return error("Failed to chown " + path);
+ }
+ }
+ }
+
+ if (prepare_app_dir(path, targetMode, uid) ||
+ prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+ prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+ return error("Failed to prepare " + path);
+ }
+
+ // Consider restorecon over contents if label changed
+ if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+ return error("Failed to restorecon " + path);
+ }
+
+ return ok();
+}
+
binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
+ int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
+ int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -437,6 +559,14 @@
if (_aidl_return != nullptr) *_aidl_return = -1;
int32_t uid = multiuser_get_uid(userId, appId);
+
+ // If previousAppId < 0, we will use the existing app data owner as previousAppUid
+ // If previousAppId == 0, we use uid as previousUid (no data migration will happen)
+ // if previousAppId > 0, an app is upgrading and changing its app ID
+ int32_t previousUid = previousAppId > 0
+ ? (int32_t) multiuser_get_uid(userId, previousAppId)
+ : (previousAppId == 0 ? uid : -1);
+
int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
@@ -447,19 +577,13 @@
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
- bool existing = (access(path.c_str(), F_OK) == 0);
- if (prepare_app_dir(path, targetMode, uid) ||
- prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
- prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
- return error("Failed to prepare " + path);
+ auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ if (!status.isOk()) {
+ return status;
}
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
- return error("Failed to restorecon " + path);
+ if (previousUid != uid) {
+ chown_app_profile_dir(packageName, appId, userId);
}
// Remember inode numbers of cache directories so that we can clear
@@ -481,19 +605,10 @@
}
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
- bool existing = (access(path.c_str(), F_OK) == 0);
- if (prepare_app_dir(path, targetMode, uid) ||
- prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
- prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
- return error("Failed to prepare " + path);
- }
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
- return error("Failed to restorecon " + path);
+ auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ if (!status.isOk()) {
+ return status;
}
if (!prepare_app_profile_dir(packageName, appId, userId)) {
@@ -503,7 +618,6 @@
return ok();
}
-
binder::Status InstalldNativeService::createAppData(
const android::os::CreateAppDataArgs& args,
android::os::CreateAppDataResult* _aidl_return) {
@@ -512,7 +626,7 @@
int64_t ceDataInode = -1;
auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
- args.seInfo, args.targetSdkVersion, &ceDataInode);
+ args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode);
_aidl_return->ceDataInode = ceDataInode;
_aidl_return->exceptionCode = status.exceptionCode();
_aidl_return->exceptionMessage = status.exceptionMessage();
@@ -523,10 +637,9 @@
const std::vector<android::os::CreateAppDataArgs>& args,
std::vector<android::os::CreateAppDataResult>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
std::vector<android::os::CreateAppDataResult> results;
- for (auto arg : args) {
+ for (const auto &arg : args) {
android::os::CreateAppDataResult result;
createAppData(arg, &result);
results.push_back(result);
@@ -624,14 +737,11 @@
}
}
if (flags & FLAG_STORAGE_DE) {
- std::string suffix = "";
- bool only_cache = false;
+ std::string suffix;
if (flags & FLAG_CLEAR_CACHE_ONLY) {
suffix = CACHE_DIR_POSTFIX;
- only_cache = true;
} else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
suffix = CODE_CACHE_DIR_POSTFIX;
- only_cache = true;
}
auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix;
@@ -1226,7 +1336,7 @@
}
if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
- seInfo, targetSdkVersion, nullptr).isOk()) {
+ /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) {
res = error("Failed to create package target");
goto fail;
}
@@ -1516,7 +1626,6 @@
const std::string& instructionSet) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PATH(codePath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
char dex_path[PKG_PATH_MAX];
@@ -2902,8 +3011,9 @@
const char* uuid_ = uuid->c_str();
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
- std::lock_guard<std::recursive_mutex> lock(mLock);
if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
return error("Failed to create CE mirror");
}
@@ -2972,8 +3082,9 @@
std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
// Unmount CE storage
- std::lock_guard<std::recursive_mutex> lock(mLock);
if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) {
if (errno != ENOENT) {
res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(),
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ae257df..8cfda01 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -47,7 +47,8 @@
binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+ int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
+ int64_t* _aidl_return);
binder::Status createAppData(
const android::os::CreateAppDataArgs& args,
diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
index 96d7faa..d5e8ee5 100644
--- a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -23,6 +23,7 @@
int userId;
int flags;
int appId;
+ int previousAppId;
@utf8InCpp String seInfo;
int targetSdkVersion;
}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index b7c0e66..2bcf2d4 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -460,8 +460,8 @@
});
}
-static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
- const std::string& location) {
+static unique_fd open_snapshot_profile(uid_t uid, const std::string& package_name,
+ const std::string& location) {
std::string profile = create_snapshot_profile_path(package_name, location);
return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
}
@@ -2563,7 +2563,7 @@
const std::string& classpath) {
int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id);
- unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
+ unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name);
if (snapshot_fd < 0) {
return false;
}
@@ -2637,7 +2637,7 @@
}
// Open and create the snapshot profile.
- unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
+ unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name);
// Collect all non empty profiles.
// The collection will traverse all applications profiles and find the non empty files.
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index ea26955..a937436 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -287,6 +287,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_);
@@ -1257,6 +1258,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_));
@@ -1320,6 +1322,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode));
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index fdbe85b..fe417a3 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -45,33 +45,6 @@
}
}
-// get the name of the generic interface we hold a reference to
-static String16 get_interface_name(sp<IBinder> service)
-{
- if (service != nullptr) {
- Parcel data, reply;
- data.markForBinder(service);
- status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);
- if (err == NO_ERROR) {
- return reply.readString16();
- }
- }
- return String16();
-}
-
-static String8 good_old_string(const String16& src)
-{
- String8 name8;
- char ch8[2];
- ch8[1] = 0;
- for (unsigned j = 0; j < src.size(); j++) {
- char16_t ch = src[j];
- if (ch < 128) ch8[0] = (char)ch;
- name8.append(ch8);
- }
- return name8;
-}
-
int main(int argc, char* const argv[])
{
bool wantsUsage = false;
@@ -132,8 +105,8 @@
String16 name = services[i];
sp<IBinder> service = sm->checkService(name);
aout << i
- << "\t" << good_old_string(name)
- << ": [" << good_old_string(get_interface_name(service)) << "]"
+ << "\t" << name
+ << ": [" << (service ? service->getInterfaceDescriptor() : String16()) << "]"
<< endl;
}
} else if (strcmp(argv[optind], "call") == 0) {
@@ -141,7 +114,7 @@
if (optind+1 < argc) {
int serviceArg = optind;
sp<IBinder> service = sm->checkService(String16(argv[optind++]));
- String16 ifName = get_interface_name(service);
+ String16 ifName = (service ? service->getInterfaceDescriptor() : String16());
int32_t code = atoi(argv[optind++]);
if (service != nullptr && ifName.size() > 0) {
Parcel data, reply;
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 3ebdeee..80c0548 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -47,6 +47,15 @@
}
cc_binary {
+ name: "servicemanager.recovery",
+ stem: "servicemanager",
+ recovery: true,
+ defaults: ["servicemanager_defaults"],
+ init_rc: ["servicemanager.recovery.rc"],
+ srcs: ["main.cpp"],
+}
+
+cc_binary {
name: "vndservicemanager",
defaults: ["servicemanager_defaults"],
init_rc: ["vndservicemanager.rc"],
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 8c1beac..2fb9c2b 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -111,6 +111,10 @@
};
int main(int argc, char** argv) {
+#ifdef __ANDROID_RECOVERY__
+ android::base::InitLogging(argv, android::base::KernelLogger);
+#endif
+
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
}
diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc
new file mode 100644
index 0000000..067faf9
--- /dev/null
+++ b/cmds/servicemanager/servicemanager.recovery.rc
@@ -0,0 +1,4 @@
+service servicemanager /system/bin/servicemanager
+ disabled
+ group system readproc
+ seclabel u:r:servicemanager:s0
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 5fe4ea1..931c5e3 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -89,6 +89,12 @@
}
prebuilt_etc {
+ name: "android.hardware.fingerprint.prebuilt.xml",
+ src: "android.hardware.fingerprint.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.location.gps.prebuilt.xml",
src: "android.hardware.location.gps.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/include/binder/Enum.h b/include/binder/Enum.h
deleted file mode 100644
index 4c25654..0000000
--- a/include/binder/Enum.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#error Do not rely on global include files. All Android cc_* programs are given access to \
- include_dirs for frameworks/native/include via global configuration, but this is legacy \
- configuration. Instead, you should have a direct dependency on libbinder OR one of your \
- dependencies should re-export libbinder headers with export_shared_lib_headers.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index dc153c9..d8101fa 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -149,6 +149,11 @@
"UtilsHost.cpp",
],
},
+ recovery: {
+ exclude_header_libs: [
+ "libandroid_runtime_vm_headers",
+ ],
+ },
},
aidl: {
@@ -197,7 +202,7 @@
sanitize: {
misc_undefined: ["integer"],
},
- min_sdk_version: "29",
+ min_sdk_version: "30",
tidy: true,
tidy_flags: [
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index 1c8bdea..f954e74 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 32456
-arve@google.com
ctate@google.com
hackbod@google.com
maco@google.com
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 631a4b6..7027a4b 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -521,6 +521,25 @@
return memcmp(data(), other.data(), size);
}
+status_t Parcel::compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset,
+ size_t len, int* result) const {
+ if (len > INT32_MAX || thisOffset > INT32_MAX || otherOffset > INT32_MAX) {
+ // Don't accept size_t values which may have come from an inadvertent conversion from a
+ // negative int.
+ return BAD_VALUE;
+ }
+ size_t thisLimit;
+ if (__builtin_add_overflow(thisOffset, len, &thisLimit) || thisLimit > mDataSize) {
+ return BAD_VALUE;
+ }
+ size_t otherLimit;
+ if (__builtin_add_overflow(otherOffset, len, &otherLimit) || otherLimit > other.mDataSize) {
+ return BAD_VALUE;
+ }
+ *result = memcmp(data() + thisOffset, other.data() + otherOffset, len);
+ return NO_ERROR;
+}
+
bool Parcel::allowFds() const
{
return mAllowFds;
@@ -805,7 +824,7 @@
const size_t padded = pad_size(len);
- // sanity check for integer overflow
+ // check for integer overflow
if (mDataPos+padded < mDataPos) {
return nullptr;
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 53c9b78..a5a2bb1 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -29,13 +29,11 @@
#include <android-base/hex.h>
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
-#include <android_runtime/vm.h>
#include <binder/BpBinder.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/RpcTransportRaw.h>
#include <binder/Stability.h>
-#include <jni.h>
#include <utils/String8.h>
#include "FdTrigger.h"
@@ -48,6 +46,11 @@
extern "C" pid_t gettid();
#endif
+#ifndef __ANDROID_RECOVERY__
+#include <android_runtime/vm.h>
+#include <jni.h>
+#endif
+
namespace android {
using base::unique_fd;
@@ -315,6 +318,9 @@
}
namespace {
+#ifdef __ANDROID_RECOVERY__
+class JavaThreadAttacher {};
+#else
// RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If
// Android Runtime doesn't exist, no-op.
class JavaThreadAttacher {
@@ -367,6 +373,7 @@
return fn();
}
};
+#endif
} // namespace
void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult) {
@@ -374,7 +381,7 @@
if (setupResult.status == OK) {
LOG_ALWAYS_FATAL_IF(!connection, "must have connection if setup succeeded");
- JavaThreadAttacher javaThreadAttacher;
+ [[maybe_unused]] JavaThreadAttacher javaThreadAttacher;
while (true) {
status_t status = session->state()->getAndExecuteCommand(connection, session,
RpcState::CommandType::ANY);
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index ece7989..bffab5e 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -27,4 +27,7 @@
@utf8InCpp String diskImagePath;
long versionCode;
@utf8InCpp String versionName;
+ boolean hasBootClassPathJars;
+ boolean hasDex2OatBootClassPathJars;
+ boolean hasSystemServerClassPathJars;
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index ff90b30..7d14315 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -129,48 +129,50 @@
#endif
-#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\
- const ::android::StaticString16 \
- I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\
- const ::android::String16 I##INTERFACE::descriptor( \
- I##INTERFACE##_descriptor_static_str16); \
- const ::android::String16& \
- I##INTERFACE::getInterfaceDescriptor() const { \
- return I##INTERFACE::descriptor; \
- } \
- ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
- const ::android::sp<::android::IBinder>& obj) \
- { \
- ::android::sp<I##INTERFACE> intr; \
- if (obj != nullptr) { \
- intr = ::android::sp<I##INTERFACE>::cast( \
- obj->queryLocalInterface(I##INTERFACE::descriptor)); \
- if (intr == nullptr) { \
- intr = ::android::sp<Bp##INTERFACE>::make(obj); \
- } \
- } \
- return intr; \
- } \
- std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl; \
- bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\
- { \
- /* Only one user of this interface can use this function */ \
- /* at a time. This is a heuristic to detect if two different */ \
- /* users in the same process use this function. */ \
- assert(!I##INTERFACE::default_impl); \
- if (impl) { \
- I##INTERFACE::default_impl = std::move(impl); \
- return true; \
- } \
- return false; \
- } \
- const std::unique_ptr<I##INTERFACE>& I##INTERFACE::getDefaultImpl() \
- { \
- return I##INTERFACE::default_impl; \
- } \
- I##INTERFACE::I##INTERFACE() { } \
- I##INTERFACE::~I##INTERFACE() { } \
+// Macro to be used by both IMPLEMENT_META_INTERFACE and IMPLEMENT_META_NESTED_INTERFACE
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(ITYPE, INAME, BPTYPE) \
+ const ::android::String16& ITYPE::getInterfaceDescriptor() const { return ITYPE::descriptor; } \
+ ::android::sp<ITYPE> ITYPE::asInterface(const ::android::sp<::android::IBinder>& obj) { \
+ ::android::sp<ITYPE> intr; \
+ if (obj != nullptr) { \
+ intr = ::android::sp<ITYPE>::cast(obj->queryLocalInterface(ITYPE::descriptor)); \
+ if (intr == nullptr) { \
+ intr = ::android::sp<BPTYPE>::make(obj); \
+ } \
+ } \
+ return intr; \
+ } \
+ std::unique_ptr<ITYPE> ITYPE::default_impl; \
+ bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) { \
+ /* Only one user of this interface can use this function */ \
+ /* at a time. This is a heuristic to detect if two different */ \
+ /* users in the same process use this function. */ \
+ assert(!ITYPE::default_impl); \
+ if (impl) { \
+ ITYPE::default_impl = std::move(impl); \
+ return true; \
+ } \
+ return false; \
+ } \
+ const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \
+ ITYPE::INAME() {} \
+ ITYPE::~INAME() {}
+// Macro for an interface type.
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ const ::android::StaticString16 I##INTERFACE##_descriptor_static_str16( \
+ __IINTF_CONCAT(u, NAME)); \
+ const ::android::String16 I##INTERFACE::descriptor(I##INTERFACE##_descriptor_static_str16); \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(I##INTERFACE, I##INTERFACE, Bp##INTERFACE)
+
+// Macro for "nested" interface type.
+// For example,
+// class Parent .. { class INested .. { }; };
+// DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(Parent, Nested, "Parent.INested")
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(PARENT, INTERFACE, NAME) \
+ const ::android::String16 PARENT::I##INTERFACE::descriptor(NAME); \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(PARENT::I##INTERFACE, I##INTERFACE, \
+ PARENT::Bp##INTERFACE)
#define CHECK_INTERFACE(interface, data, reply) \
do { \
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index d90e803..9670d7b 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -81,6 +81,8 @@
size_t start, size_t len);
int compareData(const Parcel& other);
+ status_t compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset,
+ size_t length, int* result) const;
bool allowFds() const;
bool pushAllowFds(bool allowFds);
@@ -205,6 +207,23 @@
status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
+ // Write an IInterface or a vector of IInterface's
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t writeStrongBinder(const sp<T>& val) {
+ return writeStrongBinder(T::asBinder(val));
+ }
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t writeStrongBinderVector(const std::vector<sp<T>>& val) {
+ return writeData(val);
+ }
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t writeStrongBinderVector(const std::optional<std::vector<sp<T>>>& val) {
+ return writeData(val);
+ }
+
// Write an Enum vector with underlying type int8_t.
// Does not use padding; each byte is contiguous.
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
@@ -419,6 +438,16 @@
status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t readStrongBinderVector(std::vector<sp<T>>* val) const {
+ return readData(val);
+ }
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t readStrongBinderVector(std::optional<std::vector<sp<T>>>* val) const {
+ return readData(val);
+ }
status_t readByteVector(std::optional<std::vector<int8_t>>* val) const;
status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 34f1cbf..5baa4d7 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -32,9 +32,11 @@
bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
void* param);
-// Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server, calls readyCallback with a given param, and
-// then joins before returning.
+// Starts an RPC server on a given port and a given root IBinder factory.
+// RunRpcServerWithFactory acts like RunRpcServerCallback, but instead of
+// assigning single root IBinder object to all connections, factory is called
+// whenever a client connects, making it possible to assign unique IBinder
+// object to each client.
bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
void* factoryContext, unsigned int port);
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 81aa551..6949c2c 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -104,6 +104,17 @@
return {};
}
+// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char
+static std::string SanitizeString(const String16& str) {
+ std::string sanitized{String8(str)};
+ for (auto& c : sanitized) {
+ if (!isprint(c)) {
+ c = '?';
+ }
+ }
+ return sanitized;
+}
+
bool AIBinder::associateClass(const AIBinder_Class* clazz) {
if (clazz == nullptr) return false;
@@ -118,7 +129,7 @@
if (descriptor != newDescriptor) {
if (getBinder()->isBinderAlive()) {
LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
- << "' but descriptor is actually '" << descriptor << "'.";
+ << "' but descriptor is actually '" << SanitizeString(descriptor) << "'.";
} else {
// b/155793159
LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
@@ -555,6 +566,10 @@
return ::android::IPCThreadState::self()->getCallingPid();
}
+bool AIBinder_isHandlingTransaction() {
+ return ::android::IPCThreadState::self()->getServingStackPointer() != nullptr;
+}
+
void AIBinder_incStrong(AIBinder* binder) {
if (binder == nullptr) {
return;
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 0ad400b..c903998 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -365,6 +365,8 @@
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
+ ScopedFileDescriptor dup() const { return ScopedFileDescriptor(::dup(get())); }
+
bool operator!=(const ScopedFileDescriptor& rhs) const { return get() != rhs.get(); }
bool operator<(const ScopedFileDescriptor& rhs) const { return get() < rhs.get(); }
bool operator<=(const ScopedFileDescriptor& rhs) const { return get() <= rhs.get(); }
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 5de64f8..09411e7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -190,9 +190,9 @@
BnCInterface() {}
virtual ~BnCInterface() {}
- SpAIBinder asBinder() override;
+ SpAIBinder asBinder() override final;
- bool isRemote() override { return false; }
+ bool isRemote() override final { return false; }
protected:
/**
@@ -215,9 +215,9 @@
explicit BpCInterface(const SpAIBinder& binder) : mBinder(binder) {}
virtual ~BpCInterface() {}
- SpAIBinder asBinder() override;
+ SpAIBinder asBinder() override final;
- bool isRemote() override { return AIBinder_isRemote(mBinder.get()); }
+ bool isRemote() override final { return AIBinder_isRemote(mBinder.get()); }
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override {
return AIBinder_dump(asBinder().get(), fd, args, numArgs);
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 2b18a0a..67623a6 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -27,15 +27,67 @@
#pragma once
#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <optional>
#include <string>
+#include <type_traits>
#include <vector>
namespace ndk {
+namespace {
+template <typename Test, template <typename...> class Ref>
+struct is_specialization : std::false_type {};
+
+template <template <typename...> class Ref, typename... Args>
+struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
+
+template <typename Test, template <typename...> class Ref>
+static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
+
+// Get the first template type from a container, the T from MyClass<T, ...>.
+template <typename T>
+struct first_template_type {
+ using type = void;
+};
+
+template <template <typename...> class V, typename T, typename... Args>
+struct first_template_type<V<T, Args...>> {
+ using type = T;
+};
+
+template <typename T>
+using first_template_type_t = typename first_template_type<T>::type;
+
+// Tells if T represents NDK interface (shared_ptr<ICInterface-derived>)
+template <typename T>
+static inline constexpr bool is_interface_v = is_specialization_v<T, std::shared_ptr>&&
+ std::is_base_of_v<::ndk::ICInterface, first_template_type_t<T>>;
+
+// Tells if T represents NDK parcelable with readFromParcel/writeToParcel methods defined
+template <typename T, typename = void>
+struct is_parcelable : std::false_type {};
+
+template <typename T>
+struct is_parcelable<
+ T, std::void_t<decltype(std::declval<T>().readFromParcel(std::declval<const AParcel*>())),
+ decltype(std::declval<T>().writeToParcel(std::declval<AParcel*>()))>>
+ : std::true_type {};
+
+template <typename T>
+static inline constexpr bool is_parcelable_v = is_parcelable<T>::value;
+
+// Tells if T represents nullable NDK parcelable (optional<parcelable> or unique_ptr<parcelable>)
+template <typename T>
+static inline constexpr bool is_nullable_parcelable_v = is_parcelable_v<first_template_type_t<T>> &&
+ (is_specialization_v<T, std::optional> ||
+ is_specialization_v<T, std::unique_ptr>);
+
+} // namespace
+
/**
* This retrieves and allocates a vector to size 'length' and returns the underlying buffer.
*/
@@ -429,11 +481,19 @@
*/
template <typename P>
static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
+ if constexpr (is_interface_v<P>) {
+ if (!p) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return first_template_type_t<P>::writeToParcel(parcel, p);
+ } else {
+ static_assert(is_parcelable_v<P>);
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p.writeToParcel(parcel);
}
- return p.writeToParcel(parcel);
}
/**
@@ -441,85 +501,81 @@
*/
template <typename P>
static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
+ if constexpr (is_interface_v<P>) {
+ binder_status_t status = first_template_type_t<P>::readFromParcel(parcel, p);
+ if (status == STATUS_OK) {
+ if (!*p) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ }
return status;
+ } else {
+ static_assert(is_parcelable_v<P>);
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return p->readFromParcel(parcel);
}
- if (null == 0) {
- return STATUS_UNEXPECTED_NULL;
- }
- return p->readFromParcel(parcel);
}
/**
* Convenience API for writing a nullable parcelable.
*/
template <typename P>
-static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
- const std::optional<P>& p) {
- if (p == std::nullopt) {
- return AParcel_writeInt32(parcel, 0); // null
+static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, const P& p) {
+ if constexpr (is_interface_v<P>) {
+ return first_template_type_t<P>::writeToParcel(parcel, p);
+ } else {
+ static_assert(is_nullable_parcelable_v<P>);
+ if (!p) {
+ return AParcel_writeInt32(parcel, 0); // null
+ }
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p->writeToParcel(parcel);
}
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
- }
- return p->writeToParcel(parcel);
-}
-
-/**
- * Convenience API for writing a nullable parcelable.
- */
-template <typename P>
-static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
- const std::unique_ptr<P>& p) {
- if (!p) {
- return AParcel_writeInt32(parcel, 0); // null
- }
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
- }
- return p->writeToParcel(parcel);
}
/**
* Convenience API for reading a nullable parcelable.
*/
template <typename P>
-static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
- std::optional<P>* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
- return status;
+static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, P* p) {
+ if constexpr (is_interface_v<P>) {
+ return first_template_type_t<P>::readFromParcel(parcel, p);
+ } else if constexpr (is_specialization_v<P, std::optional>) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ *p = std::nullopt;
+ return STATUS_OK;
+ }
+ *p = std::optional<first_template_type_t<P>>(first_template_type_t<P>{});
+ return (*p)->readFromParcel(parcel);
+ } else {
+ static_assert(is_specialization_v<P, std::unique_ptr>);
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ p->reset();
+ return STATUS_OK;
+ }
+ *p = std::make_unique<first_template_type_t<P>>();
+ return (*p)->readFromParcel(parcel);
}
- if (null == 0) {
- *p = std::nullopt;
- return STATUS_OK;
- }
- *p = std::optional<P>(P{});
- return (*p)->readFromParcel(parcel);
-}
-
-/**
- * Convenience API for reading a nullable parcelable.
- */
-template <typename P>
-static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
- std::unique_ptr<P>* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
- return status;
- }
- if (null == 0) {
- p->reset();
- return STATUS_OK;
- }
- *p = std::make_unique<P>();
- return (*p)->readFromParcel(parcel);
}
/**
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 43533c5..565542b 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -393,6 +393,14 @@
pid_t AIBinder_getCallingPid() __INTRODUCED_IN(29);
/**
+ * Determine whether the current thread is currently executing an incoming transaction.
+ *
+ * \return true if the current thread is currently executing an incoming transaction, and false
+ * otherwise.
+ */
+bool AIBinder_isHandlingTransaction() __INTRODUCED_IN(33);
+
+/**
* This can only be called if a strong reference to this object already exists in process.
*
* Available since API level 29.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 64170af..197c0a1 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -119,11 +119,11 @@
AIBinder_setRequestingSid; # apex
AParcel_markSensitive; # systemapi llndk
AServiceManager_forEachDeclaredInstance; # apex llndk
- AServiceManager_forceLazyServicesPersist; # llndk
+ AServiceManager_forceLazyServicesPersist; # apex llndk
AServiceManager_isDeclared; # apex llndk
AServiceManager_isUpdatableViaApex; # apex
AServiceManager_reRegister; # llndk
- AServiceManager_registerLazyService; # llndk
+ AServiceManager_registerLazyService; # apex llndk
AServiceManager_setActiveServicesCallback; # llndk
AServiceManager_tryUnregister; # llndk
AServiceManager_waitForService; # apex llndk
@@ -145,6 +145,7 @@
global:
AIBinder_Class_disableInterfaceTokenHeader;
AIBinder_DeathRecipient_setOnUnlinked;
+ AIBinder_isHandlingTransaction;
AIBinder_setMinSchedulerPolicy; # llndk
AParcel_marshal;
AParcel_unmarshal;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index ecb044e..4561d6e 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -20,6 +20,28 @@
"libdowncast_rs",
],
host_supported: true,
+ vendor_available: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ "com.android.virt",
+ ],
+}
+
+rust_library {
+ name: "libbinder_tokio_rs",
+ crate_name: "binder_tokio",
+ srcs: ["binder_tokio/lib.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ "libtokio",
+ ],
+ host_supported: true,
target: {
darwin: {
enabled: false,
@@ -43,6 +65,7 @@
"libbinder_ndk",
],
host_supported: true,
+ vendor_available: true,
target: {
darwin: {
enabled: false,
@@ -84,6 +107,7 @@
"libbinder_ndk",
],
host_supported: true,
+ vendor_available: true,
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
new file mode 100644
index 0000000..91047be
--- /dev/null
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! This crate lets you use the Tokio `spawn_blocking` pool with AIDL in async
+//! Rust code.
+//!
+//! This crate works by defining a type [`Tokio`], which you can use as the
+//! generic parameter in the async version of the trait generated by the AIDL
+//! compiler.
+//! ```text
+//! use binder_tokio::Tokio;
+//!
+//! binder::get_interface::<dyn SomeAsyncInterface<Tokio>>("...").
+//! ```
+//!
+//! [`Tokio`]: crate::Tokio
+
+use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
+use binder::{FromIBinder, StatusCode};
+use std::future::Future;
+
+/// Retrieve an existing service for a particular interface, sleeping for a few
+/// seconds if it doesn't yet exist.
+pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ if binder::is_handling_transaction() {
+ // See comment in the BinderAsyncPool impl.
+ return binder::public_api::get_interface::<T>(name);
+ }
+
+ let name = name.to_string();
+ let res = tokio::task::spawn_blocking(move || {
+ binder::public_api::get_interface::<T>(&name)
+ }).await;
+
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match res {
+ Ok(Ok(service)) => Ok(service),
+ Ok(Err(err)) => Err(err),
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR),
+ }
+}
+
+/// Retrieve an existing service for a particular interface, or start it if it
+/// is configured as a dynamic service and isn't yet started.
+pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ if binder::is_handling_transaction() {
+ // See comment in the BinderAsyncPool impl.
+ return binder::public_api::wait_for_interface::<T>(name);
+ }
+
+ let name = name.to_string();
+ let res = tokio::task::spawn_blocking(move || {
+ binder::public_api::wait_for_interface::<T>(&name)
+ }).await;
+
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match res {
+ Ok(Ok(service)) => Ok(service),
+ Ok(Err(err)) => Err(err),
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR),
+ }
+}
+
+/// Use the Tokio `spawn_blocking` pool with AIDL.
+pub enum Tokio {}
+
+impl BinderAsyncPool for Tokio {
+ fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
+ where
+ F1: FnOnce() -> A,
+ F2: FnOnce(A) -> Fut,
+ Fut: Future<Output = Result<B, E>>,
+ F1: Send + 'static,
+ F2: Send + 'a,
+ Fut: Send + 'a,
+ A: Send + 'static,
+ B: Send + 'a,
+ E: From<crate::StatusCode>,
+ {
+ if binder::is_handling_transaction() {
+ // We are currently on the thread pool for a binder server, so we should execute the
+ // transaction on the current thread so that the binder kernel driver is able to apply
+ // its deadlock prevention strategy to the sub-call.
+ //
+ // This shouldn't cause issues with blocking the thread as only one task will run in a
+ // call to `block_on`, so there aren't other tasks to block.
+ let result = spawn_me();
+ Box::pin(after_spawn(result))
+ } else {
+ let handle = tokio::task::spawn_blocking(spawn_me);
+ Box::pin(async move {
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match handle.await {
+ Ok(res) => after_spawn(res).await,
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
+ }
+ })
+ }
+ }
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index cc5dd06..d09ac83 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -17,7 +17,7 @@
//! Trait definitions for binder objects
use crate::error::{status_t, Result, StatusCode};
-use crate::parcel::{OwnedParcel, Parcel};
+use crate::parcel::{Parcel, BorrowedParcel};
use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
use crate::sys;
@@ -129,7 +129,7 @@
/// Handle and reply to a request to invoke a transaction on this object.
///
/// `reply` may be [`None`] if the sender does not expect a reply.
- fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>;
+ fn on_transact(&self, code: TransactionCode, data: &BorrowedParcel<'_>, reply: &mut BorrowedParcel<'_>) -> Result<()>;
/// Handle a request to invoke the dump transaction on this
/// object.
@@ -167,6 +167,7 @@
fn ping_binder(&mut self) -> Result<()>;
/// Indicate that the service intends to receive caller security contexts.
+ #[cfg(not(android_vndk))]
fn set_requesting_sid(&mut self, enable: bool);
/// Dump this object to the given file handle
@@ -177,25 +178,25 @@
fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
/// Create a Parcel that can be used with `submit_transact`.
- fn prepare_transact(&self) -> Result<OwnedParcel>;
+ fn prepare_transact(&self) -> Result<Parcel>;
/// Perform a generic operation with the object.
///
- /// The provided [`OwnedParcel`] must have been created by a call to
+ /// The provided [`Parcel`] must have been created by a call to
/// `prepare_transact` on the same binder.
///
/// # Arguments
///
/// * `code` - Transaction code for the operation.
- /// * `data` - [`OwnedParcel`] with input data.
+ /// * `data` - [`Parcel`] with input data.
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)).
fn submit_transact(
&self,
code: TransactionCode,
- data: OwnedParcel,
+ data: Parcel,
flags: TransactionFlags,
- ) -> Result<OwnedParcel>;
+ ) -> Result<Parcel>;
/// Perform a generic operation with the object. This is a convenience
/// method that internally calls `prepare_transact` followed by
@@ -206,15 +207,15 @@
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY))
/// * `input_callback` A callback for building the `Parcel`.
- fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+ fn transact<F: FnOnce(BorrowedParcel<'_>) -> Result<()>>(
&self,
code: TransactionCode,
flags: TransactionFlags,
input_callback: F,
) -> Result<Parcel> {
let mut parcel = self.prepare_transact()?;
- input_callback(&mut parcel.borrowed())?;
- self.submit_transact(code, parcel, flags).map(OwnedParcel::into_parcel)
+ input_callback(parcel.borrowed())?;
+ self.submit_transact(code, parcel, flags)
}
}
@@ -475,8 +476,8 @@
/// fn on_transact(
/// &self,
/// code: TransactionCode,
-/// data: &Parcel,
-/// reply: &mut Parcel,
+/// data: &BorrowedParcel,
+/// reply: &mut BorrowedParcel,
/// ) -> Result<()> {
/// // ...
/// }
@@ -635,6 +636,7 @@
pub struct BinderFeatures {
/// Indicates that the service intends to receive caller security contexts. This must be true
/// for `ThreadState::with_calling_sid` to work.
+ #[cfg(not(android_vndk))]
pub set_requesting_sid: bool,
// Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility
// when new fields are added. #[non_exhaustive] doesn't work because it prevents struct
@@ -655,13 +657,13 @@
/// have the following type:
///
/// ```
-/// # use binder::{Interface, TransactionCode, Parcel};
+/// # use binder::{Interface, TransactionCode, BorrowedParcel};
/// # trait Placeholder {
/// fn on_transact(
/// service: &dyn Interface,
/// code: TransactionCode,
-/// data: &Parcel,
-/// reply: &mut Parcel,
+/// data: &BorrowedParcel,
+/// reply: &mut BorrowedParcel,
/// ) -> binder::Result<()>;
/// # }
/// ```
@@ -676,7 +678,7 @@
/// using the provided function, `on_transact`.
///
/// ```
-/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel};
+/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, BorrowedParcel};
///
/// pub trait IServiceManager: Interface {
/// // remote methods...
@@ -692,8 +694,8 @@
/// fn on_transact(
/// service: &dyn IServiceManager,
/// code: TransactionCode,
-/// data: &Parcel,
-/// reply: &mut Parcel,
+/// data: &BorrowedParcel,
+/// reply: &mut BorrowedParcel,
/// ) -> binder::Result<()> {
/// // ...
/// Ok(())
@@ -713,12 +715,14 @@
$interface:path[$descriptor:expr] {
native: $native:ident($on_transact:path),
proxy: $proxy:ident,
+ $(async: $async_interface:ident,)?
}
} => {
$crate::declare_binder_interface! {
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ $(async: $async_interface,)?
stability: $crate::Stability::default(),
}
}
@@ -728,6 +732,7 @@
$interface:path[$descriptor:expr] {
native: $native:ident($on_transact:path),
proxy: $proxy:ident,
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -735,6 +740,7 @@
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -746,6 +752,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
}
} => {
$crate::declare_binder_interface! {
@@ -754,6 +761,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $crate::Stability::default(),
}
}
@@ -765,6 +773,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -776,6 +785,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -791,6 +801,8 @@
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $( async: $async_interface:ident, )?
+
stability: $stability:expr,
}
} => {
@@ -827,6 +839,7 @@
/// Create a new binder service.
pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+ #[cfg(not(android_vndk))]
$crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
@@ -837,7 +850,7 @@
$descriptor
}
- fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> {
+ fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> {
match $on_transact(&*self.0, code, data, reply) {
// The C++ backend converts UNEXPECTED_NULL into an exception
Err($crate::StatusCode::UNEXPECTED_NULL) => {
@@ -912,19 +925,19 @@
where
dyn $interface: $crate::Interface
{
- fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
impl $crate::parcel::SerializeOption for dyn $interface + '_ {
- fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
- impl std::fmt::Debug for dyn $interface {
+ impl std::fmt::Debug for dyn $interface + '_ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(stringify!($interface))
}
@@ -938,6 +951,73 @@
.expect(concat!("Error cloning interface ", stringify!($interface)))
}
}
+
+ $(
+ // Async interface trait implementations.
+ impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
+ fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
+ use $crate::AssociateClass;
+
+ let existing_class = ibinder.get_class();
+ if let Some(class) = existing_class {
+ if class != <$native as $crate::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ {
+ // The binder object's descriptor string matches what we
+ // expect. We still need to treat this local or already
+ // associated object as remote, because we can't cast it
+ // into a Rust service object without a matching class
+ // pointer.
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ }
+ }
+
+ if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ let service: $crate::Result<$crate::Binder<$native>> =
+ std::convert::TryFrom::try_from(ibinder.clone());
+ if let Ok(service) = service {
+ // We were able to associate with our expected class and
+ // the service is local.
+ todo!()
+ //return Ok($crate::Strong::new(Box::new(service)));
+ } else {
+ // Service is remote
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ }
+ }
+
+ Err($crate::StatusCode::BAD_TYPE.into())
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
+ fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ let binder = $crate::Interface::as_binder(self);
+ parcel.write(&binder)
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ parcel.write(&this.map($crate::Interface::as_binder))
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.pad(stringify!($async_interface))
+ }
+ }
+
+ /// Convert a &dyn $async_interface to Strong<dyn $async_interface>
+ impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> {
+ type Owned = $crate::Strong<dyn $async_interface<P>>;
+ fn to_owned(&self) -> Self::Owned {
+ self.as_binder().into_interface()
+ .expect(concat!("Error cloning interface ", stringify!($async_interface)))
+ }
+ }
+ )?
};
}
@@ -963,26 +1043,26 @@
}
impl $crate::parcel::Serialize for $enum {
- fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
parcel.write(&self.0)
}
}
impl $crate::parcel::SerializeArray for $enum {
- fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
<$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
}
}
impl $crate::parcel::Deserialize for $enum {
- fn deserialize(parcel: &$crate::parcel::Parcel) -> $crate::Result<Self> {
+ fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> {
parcel.read().map(Self)
}
}
impl $crate::parcel::DeserializeArray for $enum {
- fn deserialize_array(parcel: &$crate::parcel::Parcel) -> $crate::Result<Option<Vec<Self>>> {
+ fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> {
let v: Option<Vec<$backing>> =
<$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
Ok(v.map(|v| v.into_iter().map(Self).collect()))
diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs
new file mode 100644
index 0000000..214c0b5
--- /dev/null
+++ b/libs/binder/rust/src/binder_async.rs
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use std::future::Future;
+use std::pin::Pin;
+
+/// A type alias for a pinned, boxed future that lets you write shorter code without littering it
+/// with Pin and Send bounds.
+pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
+
+/// A thread pool for running binder transactions.
+pub trait BinderAsyncPool {
+ /// This function should conceptually behave like this:
+ ///
+ /// ```text
+ /// let result = spawn_thread(|| spawn_me()).await;
+ /// return after_spawn(result).await;
+ /// ```
+ ///
+ /// If the spawning fails for some reason, the method may also skip the `after_spawn` closure
+ /// and immediately return an error.
+ ///
+ /// The only difference between different implementations should be which
+ /// `spawn_thread` method is used. For Tokio, it would be `tokio::task::spawn_blocking`.
+ ///
+ /// This method has the design it has because the only way to define a trait that
+ /// allows the return type of the spawn to be chosen by the caller is to return a
+ /// boxed `Future` trait object, and including `after_spawn` in the trait function
+ /// allows the caller to avoid double-boxing if they want to do anything to the value
+ /// returned from the spawned thread.
+ fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
+ where
+ F1: FnOnce() -> A,
+ F2: FnOnce(A) -> Fut,
+ Fut: Future<Output = Result<B, E>>,
+ F1: Send + 'static,
+ F2: Send + 'a,
+ Fut: Send + 'a,
+ A: Send + 'static,
+ B: Send + 'a,
+ E: From<crate::StatusCode>;
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 81b620e..b94dfa1 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -50,8 +50,8 @@
//! fn on_transact(
//! service: &dyn ITest,
//! code: TransactionCode,
-//! _data: &Parcel,
-//! reply: &mut Parcel,
+//! _data: &BorrowedParcel,
+//! reply: &mut BorrowedParcel,
//! ) -> binder::Result<()> {
//! match code {
//! SpIBinder::FIRST_CALL_TRANSACTION => {
@@ -98,6 +98,7 @@
#[macro_use]
mod binder;
+mod binder_async;
mod error;
mod native;
mod state;
@@ -111,9 +112,10 @@
Stability, Strong, TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION,
FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
};
+pub use crate::binder_async::{BoxFuture, BinderAsyncPool};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder};
-pub use parcel::{OwnedParcel, Parcel};
+pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder};
+pub use parcel::{BorrowedParcel, Parcel};
pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
pub use state::{ProcessState, ThreadState};
@@ -133,8 +135,9 @@
wait_for_interface,
};
pub use super::{
- BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder,
- Status, StatusCode, Strong, ThreadState, Weak, WpIBinder,
+ BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
+ Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
+ WpIBinder,
};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index a91092e..b7c7ae4 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -18,7 +18,7 @@
AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode,
};
use crate::error::{status_result, status_t, Result, StatusCode};
-use crate::parcel::{Parcel, Serialize};
+use crate::parcel::{BorrowedParcel, Serialize};
use crate::proxy::SpIBinder;
use crate::sys;
@@ -161,8 +161,8 @@
/// # fn on_transact(
/// # service: &dyn IBar,
/// # code: TransactionCode,
- /// # data: &Parcel,
- /// # reply: &mut Parcel,
+ /// # data: &BorrowedParcel,
+ /// # reply: &mut BorrowedParcel,
/// # ) -> binder::Result<()> {
/// # Ok(())
/// # }
@@ -212,7 +212,7 @@
/// Mark this binder object with local stability, which is vendor if we are
/// building for the VNDK and system otherwise.
- #[cfg(vendor_ndk)]
+ #[cfg(any(vendor_ndk, android_vndk))]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
@@ -223,7 +223,7 @@
/// Mark this binder object with local stability, which is vendor if we are
/// building for the VNDK and system otherwise.
- #[cfg(not(vendor_ndk))]
+ #[cfg(not(any(vendor_ndk, android_vndk)))]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
@@ -277,8 +277,8 @@
reply: *mut sys::AParcel,
) -> status_t {
let res = {
- let mut reply = Parcel::borrowed(reply).unwrap();
- let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap();
+ let mut reply = BorrowedParcel::from_raw(reply).unwrap();
+ let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap();
let object = sys::AIBinder_getUserData(binder);
let binder: &T = &*(object as *const T);
binder.on_transact(code, &data, &mut reply)
@@ -384,7 +384,7 @@
}
impl<B: Remotable> Serialize for Binder<B> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write_binder(Some(&self.as_binder()))
}
}
@@ -503,8 +503,8 @@
fn on_transact(
&self,
_code: TransactionCode,
- _data: &Parcel,
- _reply: &mut Parcel,
+ _data: &BorrowedParcel<'_>,
+ _reply: &mut BorrowedParcel<'_>,
) -> Result<()> {
Ok(())
}
@@ -517,3 +517,12 @@
}
impl Interface for () {}
+
+/// Determine whether the current thread is currently executing an incoming
+/// transaction.
+pub fn is_handling_transaction() -> bool {
+ unsafe {
+ // Safety: This method is always safe to call.
+ sys::AIBinder_isHandlingTransaction()
+ }
+}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 9dba950..206b90c 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -21,11 +21,10 @@
use crate::proxy::SpIBinder;
use crate::sys;
-use std::cell::RefCell;
use std::convert::TryInto;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
-use std::ptr;
+use std::ptr::{self, NonNull};
use std::fmt;
mod file_descriptor;
@@ -46,41 +45,42 @@
/// other side of the IPC, and references to live Binder objects that will
/// result in the other side receiving a proxy Binder connected with the
/// original Binder in the Parcel.
-pub enum Parcel {
- /// Owned parcel pointer
- Owned(*mut sys::AParcel),
- /// Borrowed parcel pointer (will not be destroyed on drop)
- Borrowed(*mut sys::AParcel),
-}
-
-/// A variant of Parcel that is known to be owned.
-pub struct OwnedParcel {
- ptr: *mut sys::AParcel,
+///
+/// This type represents a parcel that is owned by Rust code.
+#[repr(transparent)]
+pub struct Parcel {
+ ptr: NonNull<sys::AParcel>,
}
/// # Safety
///
/// This type guarantees that it owns the AParcel and that all access to
-/// the AParcel happens through the OwnedParcel, so it is ok to send across
+/// the AParcel happens through the Parcel, so it is ok to send across
/// threads.
-unsafe impl Send for OwnedParcel {}
+unsafe impl Send for Parcel {}
-/// A variant of Parcel that is known to be borrowed.
+/// Container for a message (data and object references) that can be sent
+/// through Binder.
+///
+/// This object is a borrowed variant of [`Parcel`]. It is a separate type from
+/// `&mut Parcel` because it is not valid to `mem::swap` two parcels.
+#[repr(transparent)]
pub struct BorrowedParcel<'a> {
- inner: Parcel,
+ ptr: NonNull<sys::AParcel>,
_lifetime: PhantomData<&'a mut Parcel>,
}
-impl OwnedParcel {
- /// Create a new empty `OwnedParcel`.
- pub fn new() -> OwnedParcel {
+impl Parcel {
+ /// Create a new empty `Parcel`.
+ pub fn new() -> Parcel {
let ptr = unsafe {
// Safety: If `AParcel_create` succeeds, it always returns
// a valid pointer. If it fails, the process will crash.
sys::AParcel_create()
};
- assert!(!ptr.is_null());
- Self { ptr }
+ Self {
+ ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer")
+ }
}
/// Create an owned reference to a parcel object from a raw pointer.
@@ -94,108 +94,43 @@
///
/// Additionally, the caller must guarantee that it is valid to take
/// ownership of the AParcel object. All future access to the AParcel
- /// must happen through this `OwnedParcel`.
+ /// must happen through this `Parcel`.
///
- /// Because `OwnedParcel` implements `Send`, the pointer must never point
- /// to any thread-local data, e.g., a variable on the stack, either directly
- /// or indirectly.
- pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<OwnedParcel> {
- ptr.as_mut().map(|ptr| Self { ptr })
+ /// Because `Parcel` implements `Send`, the pointer must never point to any
+ /// thread-local data, e.g., a variable on the stack, either directly or
+ /// indirectly.
+ pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<Parcel> {
+ NonNull::new(ptr).map(|ptr| Self { ptr })
}
/// Consume the parcel, transferring ownership to the caller.
pub(crate) fn into_raw(self) -> *mut sys::AParcel {
- let ptr = self.ptr;
+ let ptr = self.ptr.as_ptr();
let _ = ManuallyDrop::new(self);
ptr
}
- /// Convert this `OwnedParcel` into an owned `Parcel`.
- pub fn into_parcel(self) -> Parcel {
- Parcel::Owned(self.into_raw())
- }
-
/// Get a borrowed view into the contents of this `Parcel`.
pub fn borrowed(&mut self) -> BorrowedParcel<'_> {
+ // Safety: The raw pointer is a valid pointer to an AParcel, and the
+ // lifetime of the returned `BorrowedParcel` is tied to `self`, so the
+ // borrow checker will ensure that the `AParcel` can only be accessed
+ // via the `BorrowParcel` until it goes out of scope.
BorrowedParcel {
- inner: Parcel::Borrowed(self.ptr),
+ ptr: self.ptr,
_lifetime: PhantomData,
}
}
-}
-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
-/// contain a valid pointer to an `AParcel`.
-unsafe impl AsNative<sys::AParcel> for Parcel {
- fn as_native(&self) -> *const sys::AParcel {
- match *self {
- Self::Owned(x) | Self::Borrowed(x) => x,
+ /// Get an immutable borrowed view into the contents of this `Parcel`.
+ pub fn borrowed_ref(&self) -> &BorrowedParcel<'_> {
+ // Safety: Parcel and BorrowedParcel are both represented in the same
+ // way as a NonNull<sys::AParcel> due to their use of repr(transparent),
+ // so casting references as done here is valid.
+ unsafe {
+ &*(self as *const Parcel as *const BorrowedParcel<'_>)
}
}
-
- fn as_native_mut(&mut self) -> *mut sys::AParcel {
- match *self {
- Self::Owned(x) | Self::Borrowed(x) => x,
- }
- }
-}
-
-impl Parcel {
- /// Create a new empty `Parcel`.
- ///
- /// Creates a new owned empty parcel that can be written to
- /// using the serialization methods and appended to and
- /// from using `append_from` and `append_from_all`.
- pub fn new() -> Parcel {
- let parcel = unsafe {
- // Safety: If `AParcel_create` succeeds, it always returns
- // a valid pointer. If it fails, the process will crash.
- sys::AParcel_create()
- };
- assert!(!parcel.is_null());
- Self::Owned(parcel)
- }
-
- /// Create a borrowed reference to a parcel object from a raw pointer.
- ///
- /// # Safety
- ///
- /// This constructor is safe if the raw pointer parameter is either null
- /// (resulting in `None`), or a valid pointer to an `AParcel` object.
- pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> {
- ptr.as_mut().map(|ptr| Self::Borrowed(ptr))
- }
}
impl Default for Parcel {
@@ -208,14 +143,77 @@
fn clone(&self) -> Self {
let mut new_parcel = Self::new();
new_parcel
- .append_all_from(self)
+ .borrowed()
+ .append_all_from(self.borrowed_ref())
.expect("Failed to append from Parcel");
new_parcel
}
}
+impl<'a> BorrowedParcel<'a> {
+ /// Create a borrowed reference to a parcel object from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe if the raw pointer parameter is either null
+ /// (resulting in `None`), or a valid pointer to an `AParcel` object.
+ ///
+ /// Since the raw pointer is not restricted by any lifetime, the lifetime on
+ /// the returned `BorrowedParcel` object can be chosen arbitrarily by the
+ /// caller. The caller must ensure it is valid to mutably borrow the AParcel
+ /// for the duration of the lifetime that the caller chooses. Note that
+ /// since this is a mutable borrow, it must have exclusive access to the
+ /// AParcel for the duration of the borrow.
+ pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<BorrowedParcel<'a>> {
+ Some(Self {
+ ptr: NonNull::new(ptr)?,
+ _lifetime: PhantomData,
+ })
+ }
+
+ /// Get a sub-reference to this reference to the parcel.
+ pub fn reborrow(&mut self) -> BorrowedParcel<'_> {
+ // Safety: The raw pointer is a valid pointer to an AParcel, and the
+ // lifetime of the returned `BorrowedParcel` is tied to `self`, so the
+ // borrow checker will ensure that the `AParcel` can only be accessed
+ // via the `BorrowParcel` until it goes out of scope.
+ BorrowedParcel {
+ ptr: self.ptr,
+ _lifetime: PhantomData,
+ }
+ }
+}
+
+/// # Safety
+///
+/// The `Parcel` constructors guarantee that a `Parcel` object will always
+/// contain a valid pointer to an `AParcel`.
+unsafe impl AsNative<sys::AParcel> for Parcel {
+ fn as_native(&self) -> *const sys::AParcel {
+ self.ptr.as_ptr()
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AParcel {
+ self.ptr.as_ptr()
+ }
+}
+
+/// # Safety
+///
+/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object
+/// will always contain a valid pointer to an `AParcel`.
+unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> {
+ fn as_native(&self) -> *const sys::AParcel {
+ self.ptr.as_ptr()
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AParcel {
+ self.ptr.as_ptr()
+ }
+}
+
// Data serialization methods
-impl Parcel {
+impl<'a> BorrowedParcel<'a> {
/// Data written to parcelable is zero'd before being deleted or reallocated.
pub fn mark_sensitive(&mut self) {
unsafe {
@@ -224,12 +222,12 @@
}
}
- /// Write a type that implements [`Serialize`] to the `Parcel`.
+ /// Write a type that implements [`Serialize`] to the parcel.
pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
parcelable.serialize(self)
}
- /// Writes the length of a slice to the `Parcel`.
+ /// Writes the length of a slice to the parcel.
///
/// This is used in AIDL-generated client side code to indicate the
/// allocated space for an output array parameter.
@@ -242,7 +240,7 @@
}
}
- /// Perform a series of writes to the `Parcel`, prepended with the length
+ /// Perform a series of writes to the parcel, prepended with the length
/// (in bytes) of the written data.
///
/// The length `0i32` will be written to the parcel first, followed by the
@@ -256,7 +254,7 @@
///
/// ```
/// # use binder::{Binder, Interface, Parcel};
- /// # let mut parcel = Parcel::Owned(std::ptr::null_mut());
+ /// # let mut parcel = Parcel::new();
/// parcel.sized_write(|subparcel| {
/// subparcel.write(&1u32)?;
/// subparcel.write(&2u32)?;
@@ -270,14 +268,14 @@
/// [16i32, 1u32, 2u32, 3u32]
/// ```
pub fn sized_write<F>(&mut self, f: F) -> Result<()>
- where for<'a>
- F: Fn(&'a WritableSubParcel<'a>) -> Result<()>
+ where
+ for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>
{
let start = self.get_data_position();
self.write(&0i32)?;
{
- let subparcel = WritableSubParcel(RefCell::new(self));
- f(&subparcel)?;
+ let mut subparcel = WritableSubParcel(self.reborrow());
+ f(&mut subparcel)?;
}
let end = self.get_data_position();
unsafe {
@@ -294,8 +292,8 @@
/// Returns the current position in the parcel data.
pub fn get_data_position(&self) -> i32 {
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
- // and this call is otherwise safe.
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
sys::AParcel_getDataPosition(self.as_native())
}
}
@@ -303,8 +301,8 @@
/// Returns the total size of the parcel.
pub fn get_data_size(&self) -> i32 {
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
- // and this call is otherwise safe.
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
sys::AParcel_getDataSize(self.as_native())
}
}
@@ -322,11 +320,11 @@
status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
}
- /// Append a subset of another `Parcel`.
+ /// Append a subset of another parcel.
///
/// This appends `size` bytes of data from `other` starting at offset
- /// `start` to the current `Parcel`, or returns an error if not possible.
- pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> {
+ /// `start` to the current parcel, or returns an error if not possible.
+ pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> {
let status = unsafe {
// Safety: `Parcel::appendFrom` from C++ checks that `start`
// and `size` are in bounds, and returns an error otherwise.
@@ -341,33 +339,125 @@
status_result(status)
}
- /// Append the contents of another `Parcel`.
- pub fn append_all_from(&mut self, other: &Self) -> Result<()> {
- self.append_from(other, 0, other.get_data_size())
+ /// Append the contents of another parcel.
+ pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> {
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
+ let size = unsafe { sys::AParcel_getDataSize(other.as_native()) };
+ self.append_from(other, 0, size)
}
}
-/// A segment of a writable parcel, used for [`Parcel::sized_write`].
-pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>);
+/// A segment of a writable parcel, used for [`BorrowedParcel::sized_write`].
+pub struct WritableSubParcel<'a>(BorrowedParcel<'a>);
impl<'a> WritableSubParcel<'a> {
/// Write a type that implements [`Serialize`] to the sub-parcel.
- pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> {
- parcelable.serialize(&mut *self.0.borrow_mut())
+ pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
+ parcelable.serialize(&mut self.0)
+ }
+}
+
+impl Parcel {
+ /// Data written to parcelable is zero'd before being deleted or reallocated.
+ pub fn mark_sensitive(&mut self) {
+ self.borrowed().mark_sensitive()
+ }
+
+ /// Write a type that implements [`Serialize`] to the parcel.
+ pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
+ self.borrowed().write(parcelable)
+ }
+
+ /// Writes the length of a slice to the parcel.
+ ///
+ /// This is used in AIDL-generated client side code to indicate the
+ /// allocated space for an output array parameter.
+ pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> {
+ self.borrowed().write_slice_size(slice)
+ }
+
+ /// Perform a series of writes to the parcel, prepended with the length
+ /// (in bytes) of the written data.
+ ///
+ /// The length `0i32` will be written to the parcel first, followed by the
+ /// writes performed by the callback. The initial length will then be
+ /// updated to the length of all data written by the callback, plus the
+ /// size of the length elemement itself (4 bytes).
+ ///
+ /// # Examples
+ ///
+ /// After the following call:
+ ///
+ /// ```
+ /// # use binder::{Binder, Interface, Parcel};
+ /// # let mut parcel = Parcel::new();
+ /// parcel.sized_write(|subparcel| {
+ /// subparcel.write(&1u32)?;
+ /// subparcel.write(&2u32)?;
+ /// subparcel.write(&3u32)
+ /// });
+ /// ```
+ ///
+ /// `parcel` will contain the following:
+ ///
+ /// ```ignore
+ /// [16i32, 1u32, 2u32, 3u32]
+ /// ```
+ pub fn sized_write<F>(&mut self, f: F) -> Result<()>
+ where
+ for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>
+ {
+ self.borrowed().sized_write(f)
+ }
+
+ /// Returns the current position in the parcel data.
+ pub fn get_data_position(&self) -> i32 {
+ self.borrowed_ref().get_data_position()
+ }
+
+ /// Returns the total size of the parcel.
+ pub fn get_data_size(&self) -> i32 {
+ self.borrowed_ref().get_data_size()
+ }
+
+ /// Move the current read/write position in the parcel.
+ ///
+ /// # Safety
+ ///
+ /// This method is safe if `pos` is less than the current size of the parcel
+ /// data buffer. Otherwise, we are relying on correct bounds checking in the
+ /// Parcel C++ code on every subsequent read or write to this parcel. If all
+ /// accesses are bounds checked, this call is still safe, but we can't rely
+ /// on that.
+ pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
+ self.borrowed_ref().set_data_position(pos)
+ }
+
+ /// Append a subset of another parcel.
+ ///
+ /// This appends `size` bytes of data from `other` starting at offset
+ /// `start` to the current parcel, or returns an error if not possible.
+ pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> {
+ self.borrowed().append_from(other, start, size)
+ }
+
+ /// Append the contents of another parcel.
+ pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> {
+ self.borrowed().append_all_from(other)
}
}
// Data deserialization methods
-impl Parcel {
- /// Attempt to read a type that implements [`Deserialize`] from this
- /// `Parcel`.
+impl<'a> BorrowedParcel<'a> {
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel.
pub fn read<D: Deserialize>(&self) -> Result<D> {
D::deserialize(self)
}
- /// Attempt to read a type that implements [`Deserialize`] from this
- /// `Parcel` onto an existing value. This operation will overwrite the old
- /// value partially or completely, depending on how much data is available.
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel
+ /// onto an existing value. This operation will overwrite the old value
+ /// partially or completely, depending on how much data is available.
pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> {
x.deserialize_from(self)
}
@@ -400,9 +490,9 @@
/// });
/// ```
///
- pub fn sized_read<F>(&self, mut f: F) -> Result<()>
+ pub fn sized_read<F>(&self, f: F) -> Result<()>
where
- for<'a> F: FnMut(ReadableSubParcel<'a>) -> Result<()>
+ for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>
{
let start = self.get_data_position();
let parcelable_size: i32 = self.read()?;
@@ -417,7 +507,10 @@
}
let subparcel = ReadableSubParcel {
- parcel: self,
+ parcel: BorrowedParcel {
+ ptr: self.ptr,
+ _lifetime: PhantomData,
+ },
end_position: end,
};
f(subparcel)?;
@@ -431,8 +524,8 @@
Ok(())
}
- /// Read a vector size from the `Parcel` and resize the given output vector
- /// to be correctly sized for that amount of data.
+ /// Read a vector size from the parcel and resize the given output vector to
+ /// be correctly sized for that amount of data.
///
/// This method is used in AIDL-generated server side code for methods that
/// take a mutable slice reference parameter.
@@ -450,7 +543,7 @@
Ok(())
}
- /// Read a vector size from the `Parcel` and either create a correctly sized
+ /// Read a vector size from the parcel and either create a correctly sized
/// vector for that amount of data or set the output parameter to None if
/// the vector should be null.
///
@@ -478,7 +571,7 @@
/// A segment of a readable parcel, used for [`Parcel::sized_read`].
pub struct ReadableSubParcel<'a> {
- parcel: &'a Parcel,
+ parcel: BorrowedParcel<'a>,
end_position: i32,
}
@@ -488,7 +581,7 @@
// The caller should have checked this,
// but it can't hurt to double-check
assert!(self.has_more_data());
- D::deserialize(self.parcel)
+ D::deserialize(&self.parcel)
}
/// Check if the sub-parcel has more data to read
@@ -497,11 +590,82 @@
}
}
-// Internal APIs
impl Parcel {
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel.
+ pub fn read<D: Deserialize>(&self) -> Result<D> {
+ self.borrowed_ref().read()
+ }
+
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel
+ /// onto an existing value. This operation will overwrite the old value
+ /// partially or completely, depending on how much data is available.
+ pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> {
+ self.borrowed_ref().read_onto(x)
+ }
+
+ /// Safely read a sized parcelable.
+ ///
+ /// Read the size of a parcelable, compute the end position
+ /// of that parcelable, then build a sized readable sub-parcel
+ /// and call a closure with the sub-parcel as its parameter.
+ /// The closure can keep reading data from the sub-parcel
+ /// until it runs out of input data. The closure is responsible
+ /// for calling [`ReadableSubParcel::has_more_data`] to check for
+ /// more data before every read, at least until Rust generators
+ /// are stabilized.
+ /// After the closure returns, skip to the end of the current
+ /// parcelable regardless of how much the closure has read.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut parcelable = Default::default();
+ /// parcel.sized_read(|subparcel| {
+ /// if subparcel.has_more_data() {
+ /// parcelable.a = subparcel.read()?;
+ /// }
+ /// if subparcel.has_more_data() {
+ /// parcelable.b = subparcel.read()?;
+ /// }
+ /// Ok(())
+ /// });
+ /// ```
+ ///
+ pub fn sized_read<F>(&self, f: F) -> Result<()>
+ where
+ for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>
+ {
+ self.borrowed_ref().sized_read(f)
+ }
+
+ /// Read a vector size from the parcel and resize the given output vector to
+ /// be correctly sized for that amount of data.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> {
+ self.borrowed_ref().resize_out_vec(out_vec)
+ }
+
+ /// Read a vector size from the parcel and either create a correctly sized
+ /// vector for that amount of data or set the output parameter to None if
+ /// the vector should be null.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_nullable_out_vec<D: Default + Deserialize>(
+ &self,
+ out_vec: &mut Option<Vec<D>>,
+ ) -> Result<()> {
+ self.borrowed_ref().resize_nullable_out_vec(out_vec)
+ }
+}
+
+// Internal APIs
+impl<'a> BorrowedParcel<'a> {
pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
// `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
// null or a valid pointer to an `AIBinder`, both of which are
// valid, safe inputs to `AParcel_writeStrongBinder`.
@@ -521,7 +685,7 @@
pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> {
let mut binder = ptr::null_mut();
let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
// `AParcel`. We pass a valid, mutable out pointer to the `binder`
// parameter. After this call, `binder` will be either null or a
// valid pointer to an `AIBinder` owned by the caller.
@@ -541,25 +705,11 @@
impl Drop for Parcel {
fn drop(&mut self) {
// Run the C++ Parcel complete object destructor
- if let Self::Owned(ptr) = *self {
- unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. If we own the parcel, we can safely delete it
- // here.
- sys::AParcel_delete(ptr)
- }
- }
- }
-}
-
-impl Drop for OwnedParcel {
- fn drop(&mut self) {
- // Run the C++ Parcel complete object destructor
unsafe {
- // Safety: `OwnedParcel` always contains a valid pointer to an
+ // Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. Since we own the parcel, we can safely delete it
// here.
- sys::AParcel_delete(self.ptr)
+ sys::AParcel_delete(self.ptr.as_ptr())
}
}
}
@@ -571,9 +721,9 @@
}
}
-impl fmt::Debug for OwnedParcel {
+impl<'a> fmt::Debug for BorrowedParcel<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("OwnedParcel")
+ f.debug_struct("BorrowedParcel")
.finish()
}
}
@@ -595,7 +745,7 @@
assert_eq!(parcel.read::<Option<String>>(), Ok(None));
assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL));
- assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE));
+ assert_eq!(parcel.borrowed_ref().read_binder().err(), Some(StatusCode::BAD_TYPE));
parcel.write(&1i32).unwrap();
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 8bcc5d0..b0dea94 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -15,7 +15,7 @@
*/
use super::{
- Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ Deserialize, DeserializeArray, DeserializeOption, BorrowedParcel, Serialize, SerializeArray,
SerializeOption,
};
use crate::binder::AsNative;
@@ -61,7 +61,7 @@
}
impl Serialize for ParcelFileDescriptor {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let fd = self.0.as_raw_fd();
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -78,7 +78,7 @@
impl SerializeArray for ParcelFileDescriptor {}
impl SerializeOption for ParcelFileDescriptor {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
if let Some(f) = this {
f.serialize(parcel)
} else {
@@ -95,7 +95,7 @@
}
impl DeserializeOption for ParcelFileDescriptor {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
let mut fd = -1i32;
unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -125,7 +125,7 @@
}
impl Deserialize for ParcelFileDescriptor {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
Deserialize::deserialize(parcel)
.transpose()
.unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index ec00e1d..9007cba 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -16,14 +16,14 @@
use crate::binder::{AsNative, FromIBinder, Stability, Strong};
use crate::error::{status_result, status_t, Result, Status, StatusCode};
-use crate::parcel::Parcel;
+use crate::parcel::BorrowedParcel;
use crate::proxy::SpIBinder;
use crate::sys;
use std::convert::{TryFrom, TryInto};
use std::ffi::c_void;
use std::os::raw::{c_char, c_ulong};
-use std::mem::{self, MaybeUninit};
+use std::mem::{self, MaybeUninit, ManuallyDrop};
use std::ptr;
use std::slice;
@@ -39,7 +39,7 @@
/// `Serialize::serialize` and its variants are generally
/// preferred over this function, since the former also
/// prepend a header.
- fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>;
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>;
/// Internal deserialization function for parcelables.
///
@@ -47,26 +47,26 @@
/// `Deserialize::deserialize` and its variants are generally
/// preferred over this function, since the former also
/// parse the additional header.
- fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>;
+ fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>;
}
/// A struct whose instances can be written to a [`Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Serialize {
/// Serialize this instance into the given [`Parcel`].
- fn serialize(&self, parcel: &mut Parcel) -> Result<()>;
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>;
}
/// A struct whose instances can be restored from a [`Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Deserialize: Sized {
/// Deserialize an instance from the given [`Parcel`].
- fn deserialize(parcel: &Parcel) -> Result<Self>;
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>;
/// Deserialize an instance from the given [`Parcel`] onto the
/// current object. This operation will overwrite the old value
/// partially or completely, depending on how much data is available.
- fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> {
+ fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
*self = Self::deserialize(parcel)?;
Ok(())
}
@@ -80,8 +80,8 @@
// We want the default implementation for most types, but an override for
// a few special ones like `readByteArray` for `u8`.
pub trait SerializeArray: Serialize + Sized {
- /// Serialize an array of this type into the given [`Parcel`].
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ /// Serialize an array of this type into the given parcel.
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let res = unsafe {
// Safety: Safe FFI, slice will always be a safe pointer to pass.
sys::AParcel_writeParcelableArray(
@@ -111,7 +111,7 @@
let slice: &[T] = slice::from_raw_parts(array.cast(), index+1);
- let mut parcel = match Parcel::borrowed(parcel) {
+ let mut parcel = match BorrowedParcel::from_raw(parcel) {
None => return StatusCode::UNEXPECTED_NULL as status_t,
Some(p) => p,
};
@@ -126,8 +126,8 @@
/// Defaults to calling Deserialize::deserialize() manually for every element,
/// but can be overridden for custom implementations like `readByteArray`.
pub trait DeserializeArray: Deserialize {
- /// Deserialize an array of type from the given [`Parcel`].
- fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ /// Deserialize an array of type from the given parcel.
+ fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
let res = unsafe {
// Safety: Safe FFI, vec is the correct opaque type expected by
@@ -173,7 +173,7 @@
None => return StatusCode::BAD_INDEX as status_t,
};
- let parcel = match Parcel::borrowed(parcel as *mut _) {
+ let parcel = match BorrowedParcel::from_raw(parcel as *mut _) {
None => return StatusCode::UNEXPECTED_NULL as status_t,
Some(p) => p,
};
@@ -205,8 +205,8 @@
// We also use it to provide a default implementation for AIDL-generated
// parcelables.
pub trait SerializeOption: Serialize {
- /// Serialize an Option of this type into the given [`Parcel`].
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ /// Serialize an Option of this type into the given parcel.
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
if let Some(inner) = this {
parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
parcel.write(inner)
@@ -218,8 +218,8 @@
/// Helper trait for types that can be nullable when deserialized.
pub trait DeserializeOption: Deserialize {
- /// Deserialize an Option of this type from the given [`Parcel`].
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ /// Deserialize an Option of this type from the given parcel.
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
let null: i32 = parcel.read()?;
if null == NULL_PARCELABLE_FLAG {
Ok(None)
@@ -228,10 +228,10 @@
}
}
- /// Deserialize an Option of this type from the given [`Parcel`] onto the
+ /// Deserialize an Option of this type from the given parcel onto the
/// current object. This operation will overwrite the current value
/// partially or completely, depending on how much data is available.
- fn deserialize_option_from(this: &mut Option<Self>, parcel: &Parcel) -> Result<()> {
+ fn deserialize_option_from(this: &mut Option<Self>, parcel: &BorrowedParcel<'_>) -> Result<()> {
*this = Self::deserialize_option(parcel)?;
Ok(())
}
@@ -297,10 +297,23 @@
};
}
+/// Safety: All elements in the vector must be properly initialized.
+unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> {
+ // We can convert from Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T>
+ // has the same alignment and size as T, so the pointer to the vector
+ // allocation will be compatible.
+ let mut vec = ManuallyDrop::new(vec);
+ Vec::from_raw_parts(
+ vec.as_mut_ptr().cast(),
+ vec.len(),
+ vec.capacity(),
+ )
+}
+
macro_rules! impl_parcelable {
{Serialize, $ty:ty, $write_fn:path} => {
impl Serialize for $ty {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`, and any `$ty` literal value is safe to pass to
@@ -313,7 +326,7 @@
{Deserialize, $ty:ty, $read_fn:path} => {
impl Deserialize for $ty {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut val = Self::default();
unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -329,7 +342,7 @@
{SerializeArray, $ty:ty, $write_array_fn:path} => {
impl SerializeArray for $ty {
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
@@ -353,7 +366,7 @@
{DeserializeArray, $ty:ty, $read_array_fn:path} => {
impl DeserializeArray for $ty {
- fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -371,11 +384,8 @@
// Safety: We are assuming that the NDK correctly
// initialized every element of the vector by now, so we
// know that all the MaybeUninits are now properly
- // initialized. We can transmute from Vec<MaybeUninit<T>> to
- // Vec<T> because MaybeUninit<T> has the same alignment and
- // size as T, so the pointer to the vector allocation will
- // be compatible.
- mem::transmute(vec)
+ // initialized.
+ vec.map(|vec| vec_assume_init(vec))
};
Ok(vec)
}
@@ -443,19 +453,19 @@
impl DeserializeArray for bool {}
impl Serialize for u8 {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
(*self as i8).serialize(parcel)
}
}
impl Deserialize for u8 {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
i8::deserialize(parcel).map(|v| v as u8)
}
}
impl SerializeArray for u8 {
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
@@ -474,19 +484,19 @@
}
impl Serialize for i16 {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
(*self as u16).serialize(parcel)
}
}
impl Deserialize for i16 {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
u16::deserialize(parcel).map(|v| v as i16)
}
}
impl SerializeArray for i16 {
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
@@ -505,7 +515,7 @@
}
impl SerializeOption for str {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
match this {
None => unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -541,7 +551,7 @@
}
impl Serialize for str {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Some(self).serialize(parcel)
}
}
@@ -549,7 +559,7 @@
impl SerializeArray for &str {}
impl Serialize for String {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Some(self.as_str()).serialize(parcel)
}
}
@@ -557,13 +567,13 @@
impl SerializeArray for String {}
impl SerializeOption for String {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(String::as_str), parcel)
}
}
impl Deserialize for Option<String> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut vec: Option<Vec<u8>> = None;
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an `AParcel`.
@@ -591,7 +601,7 @@
impl DeserializeArray for Option<String> {}
impl Deserialize for String {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
Deserialize::deserialize(parcel)
.transpose()
.unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
@@ -601,19 +611,19 @@
impl DeserializeArray for String {}
impl<T: SerializeArray> Serialize for [T] {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeArray::serialize_array(self, parcel)
}
}
impl<T: SerializeArray> Serialize for Vec<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeArray::serialize_array(&self[..], parcel)
}
}
impl<T: SerializeArray> SerializeOption for [T] {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
if let Some(v) = this {
SerializeArray::serialize_array(v, parcel)
} else {
@@ -623,13 +633,13 @@
}
impl<T: SerializeArray> SerializeOption for Vec<T> {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(Vec::as_slice), parcel)
}
}
impl<T: DeserializeArray> Deserialize for Vec<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
DeserializeArray::deserialize_array(parcel)
.transpose()
.unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
@@ -637,25 +647,25 @@
}
impl<T: DeserializeArray> DeserializeOption for Vec<T> {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
DeserializeArray::deserialize_array(parcel)
}
}
impl Serialize for Stability {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
i32::from(*self).serialize(parcel)
}
}
impl Deserialize for Stability {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
i32::deserialize(parcel).and_then(Stability::try_from)
}
}
impl Serialize for Status {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
unsafe {
// Safety: `Parcel` always contains a valid pointer to an `AParcel`
// and `Status` always contains a valid pointer to an `AStatus`, so
@@ -670,7 +680,7 @@
}
impl Deserialize for Status {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut status_ptr = ptr::null_mut();
let ret_status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -691,56 +701,60 @@
}
impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Serialize::serialize(&**self, parcel)
}
}
impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(|b| &**b), parcel)
}
}
+impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {}
+
impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let ibinder: SpIBinder = parcel.read()?;
FromIBinder::try_from(ibinder)
}
}
impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
let ibinder: Option<SpIBinder> = parcel.read()?;
ibinder.map(FromIBinder::try_from).transpose()
}
}
+impl<T: FromIBinder + ?Sized> DeserializeArray for Strong<T> {}
+
// We need these to support Option<&T> for all T
impl<T: Serialize + ?Sized> Serialize for &T {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Serialize::serialize(*self, parcel)
}
}
impl<T: SerializeOption + ?Sized> SerializeOption for &T {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.copied(), parcel)
}
}
impl<T: SerializeOption> Serialize for Option<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(self.as_ref(), parcel)
}
}
impl<T: DeserializeOption> Deserialize for Option<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
DeserializeOption::deserialize_option(parcel)
}
- fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> {
+ fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
DeserializeOption::deserialize_option_from(self, parcel)
}
}
@@ -758,7 +772,7 @@
impl $crate::parcel::Serialize for $parcelable {
fn serialize(
&self,
- parcel: &mut $crate::parcel::Parcel,
+ parcel: &mut $crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
<Self as $crate::parcel::SerializeOption>::serialize_option(
Some(self),
@@ -772,7 +786,7 @@
impl $crate::parcel::SerializeOption for $parcelable {
fn serialize_option(
this: Option<&Self>,
- parcel: &mut $crate::parcel::Parcel,
+ parcel: &mut $crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
if let Some(this) = this {
use $crate::parcel::Parcelable;
@@ -792,13 +806,12 @@
/// `Deserialize`, `DeserializeArray` and `DeserializeOption` for
/// structured parcelables. The target type must implement the
/// `Parcelable` trait.
-/// ```
#[macro_export]
macro_rules! impl_deserialize_for_parcelable {
($parcelable:ident) => {
impl $crate::parcel::Deserialize for $parcelable {
fn deserialize(
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<Self> {
$crate::parcel::DeserializeOption::deserialize_option(parcel)
.transpose()
@@ -806,7 +819,7 @@
}
fn deserialize_from(
&mut self,
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
let status: i32 = parcel.read()?;
if status == $crate::parcel::NULL_PARCELABLE_FLAG {
@@ -822,7 +835,7 @@
impl $crate::parcel::DeserializeOption for $parcelable {
fn deserialize_option(
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<Option<Self>> {
let mut result = None;
Self::deserialize_option_from(&mut result, parcel)?;
@@ -830,7 +843,7 @@
}
fn deserialize_option_from(
this: &mut Option<Self>,
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
let status: i32 = parcel.read()?;
if status == $crate::parcel::NULL_PARCELABLE_FLAG {
@@ -847,326 +860,332 @@
}
impl<T: Serialize> Serialize for Box<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Serialize::serialize(&**self, parcel)
}
}
impl<T: Deserialize> Deserialize for Box<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
Deserialize::deserialize(parcel).map(Box::new)
}
}
impl<T: SerializeOption> SerializeOption for Box<T> {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(|inner| &**inner), parcel)
}
}
impl<T: DeserializeOption> DeserializeOption for Box<T> {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
DeserializeOption::deserialize_option(parcel).map(|t| t.map(Box::new))
}
}
-#[test]
-fn test_custom_parcelable() {
- struct Custom(u32, bool, String, Vec<String>);
+#[cfg(test)]
+mod tests {
+ use crate::Parcel;
+ use super::*;
- impl Serialize for Custom {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
- self.0.serialize(parcel)?;
- self.1.serialize(parcel)?;
- self.2.serialize(parcel)?;
- self.3.serialize(parcel)
+ #[test]
+ fn test_custom_parcelable() {
+ struct Custom(u32, bool, String, Vec<String>);
+
+ impl Serialize for Custom {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ self.0.serialize(parcel)?;
+ self.1.serialize(parcel)?;
+ self.2.serialize(parcel)?;
+ self.3.serialize(parcel)
+ }
}
- }
- impl Deserialize for Custom {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
- Ok(Custom(
- parcel.read()?,
- parcel.read()?,
- parcel.read()?,
- parcel.read::<Option<Vec<String>>>()?.unwrap(),
- ))
+ impl Deserialize for Custom {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
+ Ok(Custom(
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read::<Option<Vec<String>>>()?.unwrap(),
+ ))
+ }
}
+
+ let string8 = "Custom Parcelable".to_string();
+
+ let s1 = "str1".to_string();
+ let s2 = "str2".to_string();
+ let s3 = "str3".to_string();
+
+ let strs = vec![s1, s2, s3];
+
+ let custom = Custom(123_456_789, true, string8, strs);
+
+ let mut parcel = Parcel::new();
+ let start = parcel.get_data_position();
+
+ assert!(custom.serialize(&mut parcel.borrowed()).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let custom2 = Custom::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(custom2.0, 123_456_789);
+ assert!(custom2.1);
+ assert_eq!(custom2.2, custom.2);
+ assert_eq!(custom2.3, custom.3);
}
- let string8 = "Custom Parcelable".to_string();
+ #[test]
+ #[allow(clippy::excessive_precision)]
+ fn test_slice_parcelables() {
+ let bools = [true, false, false, true];
- let s1 = "str1".to_string();
- let s2 = "str2".to_string();
- let s3 = "str3".to_string();
+ let mut parcel = Parcel::new();
+ let start = parcel.get_data_position();
- let strs = vec![s1, s2, s3];
+ assert!(bools.serialize(&mut parcel.borrowed()).is_ok());
- let custom = Custom(123_456_789, true, string8, strs);
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
- let mut parcel = Parcel::new();
- let start = parcel.get_data_position();
+ assert_eq!(parcel.read::<u32>().unwrap(), 4);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
- assert!(custom.serialize(&mut parcel).is_ok());
+ let vec = Vec::<bool>::deserialize(parcel.borrowed_ref()).unwrap();
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
+ assert_eq!(vec, [true, false, false, true]);
+
+ let u8s = [101u8, 255, 42, 117];
+
+ let mut parcel = Parcel::new();
+ let start = parcel.get_data_position();
+
+ assert!(parcel.write(&u8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap();
+ assert_eq!(vec, [101, 255, 42, 117]);
+
+ let i8s = [-128i8, 127, 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert!(parcel.write(&i8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap();
+ assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
+
+ let u16s = [u16::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u16s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+
+ let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i16s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+
+ let u32s = [u32::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u32s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+
+ let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i32s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+
+ let u64s = [u64::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u64s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+
+ let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i64s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+
+ let f32s = [
+ std::f32::NAN,
+ std::f32::INFINITY,
+ 1.23456789,
+ std::f32::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f32s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f32>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f32s[1..]);
+
+ let f64s = [
+ std::f64::NAN,
+ std::f64::INFINITY,
+ 1.234567890123456789,
+ std::f64::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f64s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f64>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f64s[1..]);
+
+ let s1 = "Hello, Binder!";
+ let s2 = "This is a utf8 string.";
+ let s3 = "Some more text here.";
+ let s4 = "Embedded nulls \0 \0";
+
+ let strs = [s1, s2, s3, s4];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(strs.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<String>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, strs);
}
-
- let custom2 = Custom::deserialize(&parcel).unwrap();
-
- assert_eq!(custom2.0, 123_456_789);
- assert!(custom2.1);
- assert_eq!(custom2.2, custom.2);
- assert_eq!(custom2.3, custom.3);
-}
-
-#[test]
-#[allow(clippy::excessive_precision)]
-fn test_slice_parcelables() {
- let bools = [true, false, false, true];
-
- let mut parcel = Parcel::new();
- let start = parcel.get_data_position();
-
- assert!(bools.serialize(&mut parcel).is_ok());
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4);
- assert_eq!(parcel.read::<u32>().unwrap(), 1);
- assert_eq!(parcel.read::<u32>().unwrap(), 0);
- assert_eq!(parcel.read::<u32>().unwrap(), 0);
- assert_eq!(parcel.read::<u32>().unwrap(), 1);
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<bool>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [true, false, false, true]);
-
- let u8s = [101u8, 255, 42, 117];
-
- let mut parcel = Parcel::new();
- let start = parcel.get_data_position();
-
- assert!(parcel.write(&u8s[..]).is_ok());
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u8>::deserialize(&parcel).unwrap();
- assert_eq!(vec, [101, 255, 42, 117]);
-
- let i8s = [-128i8, 127, 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert!(parcel.write(&i8s[..]).is_ok());
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u8>::deserialize(&parcel).unwrap();
- assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
-
- let u16s = [u16::max_value(), 12_345, 42, 117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(u16s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u16>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
-
- let i16s = [i16::max_value(), i16::min_value(), 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(i16s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<i16>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
-
- let u32s = [u32::max_value(), 12_345, 42, 117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(u32s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u32>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
-
- let i32s = [i32::max_value(), i32::min_value(), 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(i32s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<i32>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
-
- let u64s = [u64::max_value(), 12_345, 42, 117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(u64s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u64>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
-
- let i64s = [i64::max_value(), i64::min_value(), 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(i64s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<i64>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
-
- let f32s = [
- std::f32::NAN,
- std::f32::INFINITY,
- 1.23456789,
- std::f32::EPSILON,
- ];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(f32s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<f32>::deserialize(&parcel).unwrap();
-
- // NAN != NAN so we can't use it in the assert_eq:
- assert!(vec[0].is_nan());
- assert_eq!(vec[1..], f32s[1..]);
-
- let f64s = [
- std::f64::NAN,
- std::f64::INFINITY,
- 1.234567890123456789,
- std::f64::EPSILON,
- ];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(f64s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<f64>::deserialize(&parcel).unwrap();
-
- // NAN != NAN so we can't use it in the assert_eq:
- assert!(vec[0].is_nan());
- assert_eq!(vec[1..], f64s[1..]);
-
- let s1 = "Hello, Binder!";
- let s2 = "This is a utf8 string.";
- let s3 = "Some more text here.";
- let s4 = "Embedded nulls \0 \0";
-
- let strs = [s1, s2, s3, s4];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(strs.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<String>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, strs);
}
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index bccfd2d..b4282b2 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -16,7 +16,7 @@
use crate::binder::Stability;
use crate::error::{Result, StatusCode};
-use crate::parcel::{OwnedParcel, Parcel, Parcelable};
+use crate::parcel::{Parcel, BorrowedParcel, Parcelable};
use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
use downcast_rs::{impl_downcast, DowncastSync};
@@ -50,7 +50,7 @@
parcelable: Arc<dyn AnyParcelable>,
name: String,
},
- Parcel(OwnedParcel),
+ Parcel(Parcel),
}
impl Default for ParcelableHolderData {
@@ -148,7 +148,6 @@
}
}
ParcelableHolderData::Parcel(ref mut parcel) => {
- let parcel = parcel.borrowed();
unsafe {
// Safety: 0 should always be a valid position.
parcel.set_data_position(0)?;
@@ -160,7 +159,7 @@
}
let mut parcelable = T::default();
- parcelable.read_from_parcel(&parcel)?;
+ parcelable.read_from_parcel(parcel.borrowed_ref())?;
let parcelable = Arc::new(parcelable);
let result = Arc::clone(&parcelable);
@@ -181,7 +180,7 @@
impl_deserialize_for_parcelable!(ParcelableHolder);
impl Parcelable for ParcelableHolder {
- fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write(&self.stability)?;
let mut data = self.data.lock().unwrap();
@@ -214,14 +213,13 @@
Ok(())
}
ParcelableHolderData::Parcel(ref mut p) => {
- let p = p.borrowed();
parcel.write(&p.get_data_size())?;
- parcel.append_all_from(&p)
+ parcel.append_all_from(&*p)
}
}
}
- fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> {
+ fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
self.stability = parcel.read()?;
let data_size: i32 = parcel.read()?;
@@ -242,10 +240,8 @@
.checked_add(data_size)
.ok_or(StatusCode::BAD_VALUE)?;
- let mut new_parcel = OwnedParcel::new();
- new_parcel
- .borrowed()
- .append_from(parcel, data_start, data_size)?;
+ let mut new_parcel = Parcel::new();
+ new_parcel.append_from(parcel, data_start, data_size)?;
*self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel);
unsafe {
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index a8d0c33..760d862 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -22,8 +22,7 @@
};
use crate::error::{status_result, Result, StatusCode};
use crate::parcel::{
- Deserialize, DeserializeArray, DeserializeOption, OwnedParcel, Parcel, Serialize, SerializeArray,
- SerializeOption,
+ Parcel, BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption,
};
use crate::sys;
@@ -235,7 +234,7 @@
}
impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
- fn prepare_transact(&self) -> Result<OwnedParcel> {
+ fn prepare_transact(&self) -> Result<Parcel> {
let mut input = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -255,16 +254,16 @@
// Safety: At this point, `input` is either a valid, owned `AParcel`
// pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
// taking ownership of the parcel.
- OwnedParcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
fn submit_transact(
&self,
code: TransactionCode,
- data: OwnedParcel,
+ data: Parcel,
flags: TransactionFlags,
- ) -> Result<OwnedParcel> {
+ ) -> Result<Parcel> {
let mut reply = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -299,7 +298,7 @@
// construct a `Parcel` out of it. `AIBinder_transact` passes
// ownership of the `reply` parcel to Rust, so we need to
// construct an owned variant.
- OwnedParcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
+ Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
@@ -324,6 +323,7 @@
status_result(status)
}
+ #[cfg(not(android_vndk))]
fn set_requesting_sid(&mut self, enable: bool) {
unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
}
@@ -415,13 +415,13 @@
}
impl Serialize for SpIBinder {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write_binder(Some(self))
}
}
impl SerializeOption for SpIBinder {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write_binder(this)
}
}
@@ -429,7 +429,7 @@
impl SerializeArray for SpIBinder {}
impl Deserialize for SpIBinder {
- fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> {
parcel
.read_binder()
.transpose()
@@ -438,7 +438,7 @@
}
impl DeserializeOption for SpIBinder {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<SpIBinder>> {
parcel.read_binder()
}
}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 0e05f10..0aef744 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -99,6 +99,17 @@
}
}
+ /// Determine whether the current thread is currently executing an incoming transaction.
+ ///
+ /// \return true if the current thread is currently executing an incoming transaction, and false
+ /// otherwise.
+ pub fn is_handling_transaction() -> bool {
+ unsafe {
+ // Safety: Safe FFI
+ sys::AIBinder_isHandlingTransaction()
+ }
+ }
+
/// This function makes the client's security context available to the
/// service calling this function. This can be used for access control.
/// It does not suffer from the TOCTOU issues of get_calling_pid.
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index ecc61f4..2d1175b 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -13,6 +13,8 @@
rustlibs: [
"libbinder_rs",
"libselinux_bindgen",
+ "libbinder_tokio_rs",
+ "libtokio",
],
shared_libs: [
"libselinux",
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 335e8d8..1fd2ead 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -17,7 +17,7 @@
//! Rust Binder crate integration tests
use binder::declare_binder_interface;
-use binder::parcel::Parcel;
+use binder::parcel::BorrowedParcel;
use binder::{
Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
FIRST_CALL_TRANSACTION,
@@ -154,20 +154,33 @@
fn get_selinux_context(&self) -> binder::Result<String>;
}
+/// Async trivial testing binder interface
+pub trait IATest<P>: Interface {
+ /// Returns a test string
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+
+ /// Return the arguments sent via dump
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+
+ /// Returns the caller's SELinux context
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+}
+
declare_binder_interface! {
ITest["android.os.ITest"] {
native: BnTest(on_transact),
proxy: BpTest {
x: i32 = 100
},
+ async: IATest,
}
}
fn on_transact(
service: &dyn ITest,
code: TransactionCode,
- _data: &Parcel,
- reply: &mut Parcel,
+ _data: &BorrowedParcel<'_>,
+ reply: &mut BorrowedParcel<'_>,
) -> binder::Result<()> {
match code.try_into()? {
TestTransactionCode::Test => reply.write(&service.test()?),
@@ -201,6 +214,32 @@
}
}
+impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())),
+ |reply| async move { reply?.read() }
+ )
+ }
+
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())),
+ |reply| async move { reply?.read() }
+ )
+ }
+
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())),
+ |reply| async move { reply?.read() }
+ )
+ }
+}
+
impl ITest for Binder<BnTest> {
fn test(&self) -> binder::Result<String> {
self.0.test()
@@ -215,6 +254,23 @@
}
}
+impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let res = self.0.test();
+ Box::pin(async move { res })
+ }
+
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ let res = self.0.get_dump_args();
+ Box::pin(async move { res })
+ }
+
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let res = self.0.get_selinux_context();
+ Box::pin(async move { res })
+ }
+}
+
/// Trivial testing binder interface
pub trait ITestSameDescriptor: Interface {}
@@ -228,8 +284,8 @@
fn on_transact_same_descriptor(
_service: &dyn ITestSameDescriptor,
_code: TransactionCode,
- _data: &Parcel,
- _reply: &mut Parcel,
+ _data: &BorrowedParcel<'_>,
+ _reply: &mut BorrowedParcel<'_>,
) -> binder::Result<()> {
Ok(())
}
@@ -255,7 +311,9 @@
SpIBinder, StatusCode, Strong,
};
- use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
+ use binder_tokio::Tokio;
+
+ use super::{BnTest, ITest, IATest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
pub struct ScopedServiceProcess(Child);
@@ -303,12 +361,47 @@
binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
Some(StatusCode::NAME_NOT_FOUND)
);
+ assert_eq!(
+ binder::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
// The service manager service isn't an ITest, so this must fail.
assert_eq!(
binder::get_interface::<dyn ITest>("manager").err(),
Some(StatusCode::BAD_TYPE)
);
+ assert_eq!(
+ binder::get_interface::<dyn IATest<Tokio>>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ }
+
+ #[tokio::test]
+ async fn check_services_async() {
+ let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
+ assert!(sm.is_binder_alive());
+ assert!(sm.ping_binder().is_ok());
+
+ assert!(binder::get_service("this_service_does_not_exist").is_none());
+ assert_eq!(
+ binder_tokio::get_interface::<dyn ITest>("this_service_does_not_exist").await.err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+ assert_eq!(
+ binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").await.err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+
+ // The service manager service isn't an ITest, so this must fail.
+ assert_eq!(
+ binder_tokio::get_interface::<dyn ITest>("manager").await.err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ assert_eq!(
+ binder_tokio::get_interface::<dyn IATest<Tokio>>("manager").await.err(),
+ Some(StatusCode::BAD_TYPE)
+ );
}
#[test]
@@ -323,6 +416,10 @@
binder::wait_for_interface::<dyn ITest>("manager").err(),
Some(StatusCode::BAD_TYPE)
);
+ assert_eq!(
+ binder::wait_for_interface::<dyn IATest<Tokio>>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
}
#[test]
@@ -334,6 +431,15 @@
assert_eq!(test_client.test().unwrap(), "trivial_client_test");
}
+ #[tokio::test]
+ async fn trivial_client_async() {
+ let service_name = "trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(test_client.test().await.unwrap(), "trivial_client_test");
+ }
+
#[test]
fn wait_for_trivial_client() {
let service_name = "wait_for_trivial_client_test";
@@ -343,23 +449,47 @@
assert_eq!(test_client.test().unwrap(), "wait_for_trivial_client_test");
}
+ #[tokio::test]
+ async fn wait_for_trivial_client_async() {
+ let service_name = "wait_for_trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::wait_for_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(test_client.test().await.unwrap(), "wait_for_trivial_client_test");
+ }
+
+ fn get_expected_selinux_context() -> &'static str {
+ unsafe {
+ let mut out_ptr = ptr::null_mut();
+ assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
+ assert!(!out_ptr.is_null());
+ CStr::from_ptr(out_ptr)
+ .to_str()
+ .expect("context was invalid UTF-8")
+ }
+ }
+
#[test]
fn get_selinux_context() {
let service_name = "get_selinux_context";
let _process = ScopedServiceProcess::new(service_name);
let test_client: Strong<dyn ITest> =
binder::get_interface(service_name).expect("Did not get manager binder service");
- let expected_context = unsafe {
- let mut out_ptr = ptr::null_mut();
- assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
- assert!(!out_ptr.is_null());
- CStr::from_ptr(out_ptr)
- };
assert_eq!(
test_client.get_selinux_context().unwrap(),
- expected_context
- .to_str()
- .expect("context was invalid UTF-8"),
+ get_expected_selinux_context()
+ );
+ }
+
+ #[tokio::test]
+ async fn get_selinux_context_async() {
+ let service_name = "get_selinux_context";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(
+ test_client.get_selinux_context().await.unwrap(),
+ get_expected_selinux_context()
);
}
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 66ba846..1fc761e 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -20,7 +20,7 @@
use binder::declare_binder_interface;
use binder::parcel::ParcelFileDescriptor;
use binder::{
- Binder, BinderFeatures, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
+ Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status,
StatusCode, TransactionCode,
};
@@ -111,8 +111,8 @@
fn on_transact(
_service: &dyn ReadParcelTest,
code: TransactionCode,
- parcel: &Parcel,
- reply: &mut Parcel,
+ parcel: &BorrowedParcel<'_>,
+ reply: &mut BorrowedParcel<'_>,
) -> Result<()> {
match code {
bindings::Transaction_TEST_BOOL => {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 55ad3c6..5a96b78 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -22,6 +22,7 @@
#include <aidl/IBinderRpcTest.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
#include <binder/Binder.h>
@@ -1514,7 +1515,17 @@
auto socket = rpcServer->releaseServer();
auto keepAlive = sp<BBinder>::make();
- ASSERT_EQ(OK, binder->setRpcClientDebug(std::move(socket), keepAlive));
+ auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive);
+
+ if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus)
+ << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable builds, "
+ "but get "
+ << statusToString(setRpcClientDebugStatus);
+ GTEST_SKIP();
+ }
+
+ ASSERT_EQ(OK, setRpcClientDebugStatus);
auto rpcSession = RpcSession::make();
ASSERT_EQ(OK, rpcSession->setupInetClient("127.0.0.1", port));
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index e4f57b0..32406e5 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -192,6 +192,8 @@
// only reading one binder type for now
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
+ PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::os::IServiceManager>>, readStrongBinderVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::sp<android::os::IServiceManager>>>, readStrongBinderVector),
PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
@@ -308,6 +310,15 @@
status_t status = p.hasFileDescriptorsInRange(offset, length, &result);
FUZZ_LOG() << " status: " << status << " result: " << result;
},
+ [] (const ::android::Parcel& p, uint8_t /* data */) {
+ FUZZ_LOG() << "about to call compareDataInRange() with status";
+ size_t thisOffset = p.readUint32();
+ size_t otherOffset = p.readUint32();
+ size_t length = p.readUint32();
+ int result;
+ status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result);
+ FUZZ_LOG() << " status: " << status << " result: " << result;
+ },
};
// clang-format on
#pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index c0a762d..752fcbb 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -25,6 +25,7 @@
// TODO(b/142061461): parent class
class SomeParcelable {
public:
+ binder_status_t writeToParcel(AParcel* /*parcel*/) { return STATUS_OK; }
binder_status_t readFromParcel(const AParcel* parcel) {
return AParcel_readInt32(parcel, &mValue);
}
@@ -33,6 +34,41 @@
int32_t mValue = 0;
};
+class ISomeInterface : public ::ndk::ICInterface {
+public:
+ ISomeInterface() = default;
+ virtual ~ISomeInterface() = default;
+ static binder_status_t readFromParcel(const AParcel* parcel,
+ std::shared_ptr<ISomeInterface>* instance);
+};
+
+static binder_status_t onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
+ return STATUS_UNKNOWN_TRANSACTION;
+}
+
+static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact);
+
+class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> {
+public:
+ explicit BpSomeInterface(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {}
+ virtual ~BpSomeInterface() = default;
+};
+
+binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel,
+ std::shared_ptr<ISomeInterface>* instance) {
+ ::ndk::SpAIBinder binder;
+ binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR());
+ if (status == STATUS_OK) {
+ if (AIBinder_associateClass(binder.get(), g_class)) {
+ *instance = std::static_pointer_cast<ISomeInterface>(
+ ::ndk::ICInterface::asInterface(binder.get()));
+ } else {
+ *instance = ::ndk::SharedRefBase::make<BpSomeInterface>(binder);
+ }
+ }
+ return status;
+}
+
#define PARCEL_READ(T, FUN) \
[](const NdkParcelAdapter& p, uint8_t /*data*/) { \
FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
@@ -100,6 +136,8 @@
PARCEL_READ(std::optional<std::vector<ndk::SpAIBinder>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<ndk::ScopedFileDescriptor>, ndk::AParcel_readVector),
PARCEL_READ(std::optional<std::vector<ndk::ScopedFileDescriptor>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<std::shared_ptr<ISomeInterface>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<std::shared_ptr<ISomeInterface>>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 8edf604..a626970 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -71,6 +71,7 @@
case HAL_PIXEL_FORMAT_Y8:
case HAL_PIXEL_FORMAT_Y16:
case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW12:
case HAL_PIXEL_FORMAT_RAW10:
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
case HAL_PIXEL_FORMAT_BLOB:
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index e1b1efc..46800f2 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -33,10 +33,13 @@
// using just a few large reads.
static const size_t EVENT_BUFFER_SIZE = 100;
+static constexpr nsecs_t WAITING_FOR_VSYNC_TIMEOUT = ms2ns(300);
+
DisplayEventDispatcher::DisplayEventDispatcher(
const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource,
ISurfaceComposer::EventRegistrationFlags eventRegistration)
- : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) {
+ : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false),
+ mLastVsyncCount(0), mLastScheduleVsyncTime(0) {
ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
}
@@ -86,6 +89,7 @@
}
mWaitingForVsync = true;
+ mLastScheduleVsyncTime = systemTime(SYSTEM_TIME_MONOTONIC);
}
return OK;
}
@@ -124,9 +128,21 @@
this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
vsyncEventData.id);
mWaitingForVsync = false;
+ mLastVsyncCount = vsyncCount;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
}
+ if (mWaitingForVsync) {
+ const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t vsyncScheduleDelay = currentTime - mLastScheduleVsyncTime;
+ if (vsyncScheduleDelay > WAITING_FOR_VSYNC_TIMEOUT) {
+ ALOGW("Vsync time out! vsyncScheduleDelay=%" PRId64 "ms", ns2ms(vsyncScheduleDelay));
+ mWaitingForVsync = false;
+ dispatchVsync(currentTime, vsyncDisplayId /* displayId is not used */,
+ ++mLastVsyncCount, vsyncEventData /* empty data */);
+ }
+ }
+
return 1; // keep the callback
}
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 30d19e3..b3647d6 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -301,7 +301,7 @@
// continues to use it.
sp<GraphicBuffer> buffer = new GraphicBuffer(
kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_SW_WRITE_RARELY,
+ DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
"[GLConsumer debug texture]");
uint32_t* bits;
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0d7795e..a82fc6f 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -291,6 +291,17 @@
return {};
}
+ status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId* displayId) const override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(remote()->transact, BnSurfaceComposer::GET_PRIMARY_PHYSICAL_DISPLAY_ID, data,
+ &reply);
+ uint64_t rawId;
+ SAFE_PARCEL(reply.readUint64, &rawId);
+ *displayId = PhysicalDisplayId(rawId);
+ return NO_ERROR;
+ }
+
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1713,6 +1724,16 @@
[](PhysicalDisplayId id) { return id.value; });
return reply->writeUint64Vector(rawIds);
}
+ case GET_PRIMARY_PHYSICAL_DISPLAY_ID: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ PhysicalDisplayId id;
+ status_t result = getPrimaryPhysicalDisplayId(&id);
+ if (result != NO_ERROR) {
+ ALOGE("getPrimaryPhysicalDisplayId: Failed to get id");
+ return result;
+ }
+ return reply->writeUint64(id.value);
+ }
case ADD_REGION_SAMPLING_LISTENER: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
Rect samplingArea;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 96da8ef..1377284 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -913,6 +913,10 @@
return ComposerService::getComposerService()->getPhysicalDisplayIds();
}
+status_t SurfaceComposerClient::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) {
+ return ComposerService::getComposerService()->getPrimaryPhysicalDisplayId(id);
+}
+
std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
return ComposerService::getComposerService()->getInternalDisplayId();
}
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 4ade240..08f3597 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -56,6 +56,8 @@
sp<Looper> mLooper;
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
+ uint32_t mLastVsyncCount;
+ nsecs_t mLastScheduleVsyncTime;
std::vector<FrameRateOverride> mFrameRateOverrides;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 2a3f6a4..ef5353f 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -140,6 +140,8 @@
*/
virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const = 0;
+ virtual status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const = 0;
+
// TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
std::optional<PhysicalDisplayId> getInternalDisplayId() const {
const auto displayIds = getPhysicalDisplayIds();
@@ -624,6 +626,7 @@
ON_PULL_ATOM,
ADD_TUNNEL_MODE_ENABLED_LISTENER,
REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
+ GET_PRIMARY_PHYSICAL_DISPLAY_ID,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index baa0567..4164ca3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -307,6 +307,7 @@
//! Get stable IDs for connected physical displays
static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
+ static status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*);
static std::optional<PhysicalDisplayId> getInternalDisplayId();
//! Get token for a physical display given its stable ID
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 59b0c04..630dd17 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -694,6 +694,7 @@
bool /*secure*/) override { return nullptr; }
void destroyDisplay(const sp<IBinder>& /*display */) override {}
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
+ status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override { return NO_ERROR; }
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/,
const Vector<ComposerState>& /*state*/,
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 30c42a3..fcbc16f 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -89,8 +89,15 @@
// Treblized input device config files will be located /product/usr, /system_ext/usr,
// /odm/usr or /vendor/usr.
- const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor",
- getenv("ANDROID_ROOT")};
+ // These files may also be in the com.android.input.config APEX.
+ const char* rootsForPartition[]{
+ "/product",
+ "/system_ext",
+ "/odm",
+ "/vendor",
+ "/apex/com.android.input.config/etc",
+ getenv("ANDROID_ROOT"),
+ };
for (size_t i = 0; i < size(rootsForPartition); i++) {
if (rootsForPartition[i] == nullptr) {
continue;
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 2f31888..6882ea3 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -191,7 +191,7 @@
// continues to use it.
sp<GraphicBuffer> buffer =
new GraphicBuffer(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_SW_WRITE_RARELY,
+ DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
"[EGLConsumer debug texture]");
uint32_t* bits;
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 0c5a851..9e466b6 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -26,55 +26,39 @@
namespace android {
namespace renderengine {
-std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
- RenderEngineType renderEngineType = args.renderEngineType;
-
+std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) {
// Keep the ability to override by PROPERTIES:
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
if (strcmp(prop, "gles") == 0) {
- renderEngineType = RenderEngineType::GLES;
+ args.renderEngineType = RenderEngineType::GLES;
}
if (strcmp(prop, "threaded") == 0) {
- renderEngineType = RenderEngineType::THREADED;
+ args.renderEngineType = RenderEngineType::THREADED;
}
if (strcmp(prop, "skiagl") == 0) {
- renderEngineType = RenderEngineType::SKIA_GL;
+ args.renderEngineType = RenderEngineType::SKIA_GL;
}
if (strcmp(prop, "skiaglthreaded") == 0) {
- renderEngineType = RenderEngineType::SKIA_GL_THREADED;
+ args.renderEngineType = RenderEngineType::SKIA_GL_THREADED;
}
- switch (renderEngineType) {
+ switch (args.renderEngineType) {
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
return renderengine::threaded::RenderEngineThreaded::create(
[args]() { return android::renderengine::gl::GLESRenderEngine::create(args); },
- renderEngineType);
+ args.renderEngineType);
case RenderEngineType::SKIA_GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
case RenderEngineType::SKIA_GL_THREADED: {
- // These need to be recreated, since they are a constant reference, and we need to
- // let SkiaRE know that it's running as threaded, and all GL operation will happen on
- // the same thread.
- RenderEngineCreationArgs skiaArgs =
- RenderEngineCreationArgs::Builder()
- .setPixelFormat(args.pixelFormat)
- .setImageCacheSize(args.imageCacheSize)
- .setUseColorManagerment(args.useColorManagement)
- .setEnableProtectedContext(args.enableProtectedContext)
- .setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly)
- .setSupportsBackgroundBlur(args.supportsBackgroundBlur)
- .setContextPriority(args.contextPriority)
- .setRenderEngineType(renderEngineType)
- .build();
ALOGD("Threaded RenderEngine with SkiaGL Backend");
return renderengine::threaded::RenderEngineThreaded::create(
- [skiaArgs]() {
- return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
+ [args]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(args);
},
- renderEngineType);
+ args.renderEngineType);
}
case RenderEngineType::GLES:
default:
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 5964bc3..46a7d1e 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -98,7 +98,7 @@
SKIA_GL_THREADED = 4,
};
- static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
+ static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args);
virtual ~RenderEngine() = 0;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a519786..c2a2794 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3899,6 +3899,13 @@
args->downTime, args->pointerCount,
args->pointerProperties, args->pointerCoords, 0, 0);
+ if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
+ IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
+ !mInputFilterEnabled) {
+ const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN;
+ mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime);
+ }
+
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index d634dcd..52f189c 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -50,13 +50,12 @@
* key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value).
*/
template <typename K, typename V>
-static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) {
- auto iterpair = map.equal_range(key);
-
- for (auto it = iterpair.first; it != iterpair.second; ++it) {
+static void eraseByValue(std::multimap<K, V>& map, const V& value) {
+ for (auto it = map.begin(); it != map.end();) {
if (it->second == value) {
- map.erase(it);
- break;
+ it = map.erase(it);
+ } else {
+ it++;
}
}
}
@@ -76,9 +75,7 @@
// confuse us by reporting the rest of the timeline for one of them. This should happen
// rarely, so we won't lose much data
mTimelines.erase(it);
- // In case we have another input event with a different id and at the same eventTime,
- // only erase this specific inputEventId.
- eraseByKeyAndValue(mEventTimes, eventTime, inputEventId);
+ eraseByValue(mEventTimes, inputEventId);
return;
}
mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime));
@@ -90,7 +87,8 @@
nsecs_t finishTime) {
const auto it = mTimelines.find(inputEventId);
if (it == mTimelines.end()) {
- // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do
+ // This could happen if we erased this event when duplicate events were detected. It's
+ // also possible that an app sent a bad (or late) 'Finish' signal, since it's free to do
// anything in its process. Just drop the report and move on.
return;
}
@@ -120,7 +118,8 @@
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
const auto it = mTimelines.find(inputEventId);
if (it == mTimelines.end()) {
- // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do
+ // This could happen if we erased this event when duplicate events were detected. It's
+ // also possible that an app sent a bad (or late) 'Timeline' signal, since it's free to do
// anything in its process. Just drop the report and move on.
return;
}
@@ -166,14 +165,6 @@
}
}
-void LatencyTracker::reportNow() {
- for (const auto& [inputEventId, timeline] : mTimelines) {
- mTimelineProcessor->processTimeline(timeline);
- }
- mTimelines.clear();
- mEventTimes.clear();
-}
-
std::string LatencyTracker::dump(const char* prefix) {
return StringPrintf("%sLatencyTracker:\n", prefix) +
StringPrintf("%s mTimelines.size() = %zu\n", prefix, mTimelines.size()) +
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 289b8ed..4b0c618 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -43,6 +43,12 @@
LatencyTracker(InputEventTimelineProcessor* processor);
/**
* Start keeping track of an event identified by inputEventId. This must be called first.
+ * If duplicate events are encountered (events that have the same eventId), none of them will be
+ * tracked. This is because there is not enough information to correctly track them. The api's
+ * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the
+ * eventTime. Even if eventTime was provided, there would still be a possibility of having
+ * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
+ * must drop all duplicate data.
*/
void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime);
void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
@@ -50,14 +56,6 @@
void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
- /**
- * Report all collected events immediately, even if some of them are currently incomplete
- * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future.
- * This is useful for tests. Otherwise, tests would have to inject additional "future" events,
- * which is not convenient.
- */
- void reportNow();
-
std::string dump(const char* prefix);
private:
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index e7e1937..89c0741 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -16,6 +16,7 @@
#include "../dispatcher/LatencyTracker.h"
+#include <android-base/properties.h>
#include <binder/Binder.h>
#include <gtest/gtest.h>
#include <inttypes.h>
@@ -23,11 +24,16 @@
#define TAG "LatencyTracker_test"
+using android::base::HwTimeoutMultiplier;
using android::inputdispatcher::InputEventTimeline;
using android::inputdispatcher::LatencyTracker;
namespace android::inputdispatcher {
+const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
+ android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ HwTimeoutMultiplier());
+
InputEventTimeline getTestTimeline() {
InputEventTimeline t(
/*isDown*/ true,
@@ -57,6 +63,8 @@
}
void TearDown() override {}
+ void triggerEventReporting(nsecs_t lastEventTime);
+
void assertReceivedTimeline(const InputEventTimeline& timeline);
/**
* Timelines can be received in any order (order is not guaranteed). So if we are expecting more
@@ -72,8 +80,17 @@
std::deque<InputEventTimeline> mReceivedTimelines;
};
+/**
+ * Send an event that would trigger the reporting of all of the events that are at least as old as
+ * the provided 'lastEventTime'.
+ */
+void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) {
+ const nsecs_t triggerEventTime =
+ lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
+ mTracker->trackListener(1 /*inputEventId*/, true /*isDown*/, triggerEventTime, 3 /*readTime*/);
+}
+
void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
- mTracker->reportNow();
ASSERT_FALSE(mReceivedTimelines.empty());
const InputEventTimeline& t = mReceivedTimelines.front();
ASSERT_EQ(timeline, t);
@@ -88,7 +105,6 @@
* equal element in B, and for every element in B there is an equal element in A.
*/
void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) {
- mTracker->reportNow();
ASSERT_EQ(timelines.size(), mReceivedTimelines.size());
for (const InputEventTimeline& expectedTimeline : timelines) {
bool found = false;
@@ -121,6 +137,7 @@
*/
TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/);
+ triggerEventReporting(2 /*eventTime*/);
assertReceivedTimeline(InputEventTimeline{false, 2, 3});
}
@@ -130,6 +147,7 @@
TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) {
mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/,
3 /*consumeTime*/, 4 /*finishTime*/);
+ triggerEventReporting(4 /*eventTime*/);
assertReceivedTimelines({});
}
@@ -141,6 +159,7 @@
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline);
+ triggerEventReporting(3 /*eventTime*/);
assertReceivedTimelines({});
}
@@ -155,9 +174,30 @@
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
+ triggerEventReporting(expected.eventTime);
assertReceivedTimeline(expected);
}
+/**
+ * Send 2 events with the same inputEventId, but different eventTime's. Ensure that no crash occurs,
+ * and that the tracker drops such events completely.
+ */
+TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) {
+ constexpr nsecs_t inputEventId = 1;
+ constexpr nsecs_t readTime = 3; // does not matter for this test
+ constexpr bool isDown = true; // does not matter for this test
+
+ // In the following 2 calls to trackListener, the inputEventId's are the same, but event times
+ // are different.
+ mTracker->trackListener(inputEventId, isDown, 1 /*eventTime*/, readTime);
+ mTracker->trackListener(inputEventId, isDown, 2 /*eventTime*/, readTime);
+
+ triggerEventReporting(2 /*eventTime*/);
+ // Since we sent duplicate input events, the tracker should just delete all of them, because it
+ // does not have enough information to properly track them.
+ assertReceivedTimelines({});
+}
+
TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) {
constexpr int32_t inputEventId1 = 1;
InputEventTimeline timeline1(
@@ -204,6 +244,7 @@
mTracker->trackGraphicsLatency(inputEventId2, connection2,
connectionTimeline2.graphicsTimeline);
// Now both events should be completed
+ triggerEventReporting(timeline2.eventTime);
assertReceivedTimelines({timeline1, timeline2});
}
@@ -228,6 +269,7 @@
mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline);
expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT));
+ triggerEventReporting(timeline.eventTime);
assertReceivedTimelines(expectedTimelines);
}
@@ -246,6 +288,7 @@
mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+ triggerEventReporting(expected.eventTime);
assertReceivedTimeline(
InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime});
}
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..df4db19
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+
+cc_fuzz {
+ name: "inputflinger_latencytracker_fuzzer",
+ defaults: [
+ "inputflinger_defaults",
+ ],
+ include_dirs: [
+ "frameworks/native/services/inputflinger",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libui",
+ "libutils",
+ "libinput",
+ "libinputflinger",
+ ],
+ srcs: [
+ "LatencyTrackerFuzzer.cpp",
+ ],
+}
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
new file mode 100644
index 0000000..4f066ad
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "dispatcher/LatencyTracker.h"
+
+namespace android {
+
+namespace inputdispatcher {
+
+/**
+ * A processor of InputEventTimelines that does nothing with the provided data.
+ */
+class EmptyProcessor : public InputEventTimelineProcessor {
+public:
+ /**
+ * Just ignore the provided timeline
+ */
+ void processTimeline(const InputEventTimeline& timeline) override {
+ for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) {
+ connectionTimeline.isComplete();
+ }
+ };
+};
+
+static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp,
+ std::array<sp<IBinder>, 10>& tokens) {
+ const bool useExistingToken = fdp.ConsumeBool();
+ if (useExistingToken) {
+ return tokens[fdp.ConsumeIntegralInRange<size_t>(0ul, tokens.size() - 1)];
+ }
+ return new BBinder();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ EmptyProcessor emptyProcessor;
+ LatencyTracker tracker(&emptyProcessor);
+
+ // Make some pre-defined tokens to ensure that some timelines are complete.
+ std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
+ for (size_t i = 0; i < predefinedTokens.size(); i++) {
+ predefinedTokens[i] = new BBinder();
+ }
+
+ // Randomly invoke LatencyTracker api's until randomness is exhausted.
+ while (fdp.remaining_bytes() > 0) {
+ fdp.PickValueInArray<std::function<void()>>({
+ [&]() -> void {
+ int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+ int32_t isDown = fdp.ConsumeBool();
+ nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
+ nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
+ tracker.trackListener(inputEventId, isDown, eventTime, readTime);
+ },
+ [&]() -> void {
+ int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+ sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
+ nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>();
+ nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>();
+ nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>();
+ tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime,
+ consumeTime, finishTime);
+ },
+ [&]() -> void {
+ int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+ sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ for (size_t i = 0; i < graphicsTimeline.size(); i++) {
+ graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>();
+ }
+ tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline);
+ },
+ })();
+ }
+
+ return 0;
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index e26ab11..82a9ae2 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -326,7 +326,7 @@
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
- LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
+ LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
status_t result =
mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -641,7 +641,7 @@
}
status_t VirtualDisplaySurface::refreshOutputBuffer() {
- LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
+ LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
if (mOutputProducerSlot >= 0) {
mSource[SOURCE_SINK]->cancelBuffer(
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 230810c..abc49bf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -653,6 +653,17 @@
return displayIds;
}
+status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const {
+ Mutex::Autolock lock(mStateLock);
+ const auto display = getInternalDisplayIdLocked();
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+
+ *id = *display;
+ return NO_ERROR;
+}
+
sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
Mutex::Autolock lock(mStateLock);
return getPhysicalDisplayTokenLocked(displayId);
@@ -5238,6 +5249,7 @@
case REMOVE_TUNNEL_MODE_ENABLED_LISTENER:
case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
+ case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
// which acquire the necessary permission dynamically. Don't use the permission cache
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 380f444..fa19b7f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -614,6 +614,7 @@
sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
void destroyDisplay(const sp<IBinder>& displayToken) override;
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
+ status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override EXCLUDES(mStateLock);
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
const Vector<ComposerState>& state,
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 4a69c8f..e15eae8 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -34,6 +34,8 @@
using android::hardware::graphics::common::V1_2::PixelFormat;
using android::ui::DisplayPrimaries;
+// Keep logic in sync with WindowManagerService functions that query SurfaceFlinger properties.
+// Consider exposing properties via ISurfaceComposer instead.
int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 673239d..caeff4a 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -63,18 +63,17 @@
return OK;
}
-static status_t startDisplayService() {
+static void startDisplayService() {
using android::frameworks::displayservice::V1_0::implementation::DisplayService;
using android::frameworks::displayservice::V1_0::IDisplayService;
sp<IDisplayService> displayservice = new DisplayService();
status_t err = displayservice->registerAsService();
+ // b/141930622
if (err != OK) {
- ALOGE("Could not register IDisplayService service.");
+ ALOGE("Did not register (deprecated) IDisplayService service.");
}
-
- return err;
}
int main(int, char**) {