Merge "Remove Enum.h error-only header"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 157d259..39ef0b5 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -422,9 +422,131 @@
return true;
}
+static bool chown_app_dir(const std::string& path, uid_t uid, uid_t previousUid, gid_t cacheGid) {
+ FTS* fts;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
+ return false;
+ }
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ if (p->fts_info == FTS_D && p->fts_level == 1
+ && (strcmp(p->fts_name, "cache") == 0
+ || strcmp(p->fts_name, "code_cache") == 0)) {
+ // Mark cache dirs
+ p->fts_number = 1;
+ } else {
+ // Inherit parent's number
+ p->fts_number = p->fts_parent->fts_number;
+ }
+
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (p->fts_statp->st_uid == previousUid) {
+ if (lchown(p->fts_path, uid, p->fts_number ? cacheGid : uid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ } else {
+ LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected UID "
+ << p->fts_statp->st_uid << " instead of " << previousUid;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ return true;
+}
+
+static void chown_app_profile_dir(const std::string &packageName, int32_t appId, int32_t userId) {
+ uid_t uid = multiuser_get_uid(userId, appId);
+ gid_t sharedGid = multiuser_get_shared_gid(userId, appId);
+
+ const std::string profile_dir =
+ create_primary_current_profile_package_dir_path(userId, packageName);
+ char *argv[] = { (char*) profile_dir.c_str(), nullptr };
+ if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) {
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, uid, uid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ }
+
+ const std::string ref_profile_path =
+ create_primary_reference_profile_package_dir_path(packageName);
+ argv[0] = (char *) ref_profile_path.c_str();
+ if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) {
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ if (p->fts_info == FTS_D && p->fts_level == 0) {
+ if (chown(p->fts_path, AID_SYSTEM, sharedGid) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ continue;
+ }
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, sharedGid, sharedGid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ }
+}
+
+static binder::Status createAppDataDirs(const std::string& path,
+ int32_t uid, int32_t* previousUid, int32_t cacheGid,
+ const std::string& seInfo, mode_t targetMode) {
+ struct stat st{};
+ bool existing = (stat(path.c_str(), &st) == 0);
+ if (existing) {
+ if (*previousUid < 0) {
+ // If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner
+ // of the directory as previousUid. This is required because it is not always possible
+ // to chown app data during app upgrade (e.g. secondary users' CE storage not unlocked)
+ *previousUid = st.st_uid;
+ }
+ if (*previousUid != uid) {
+ if (!chown_app_dir(path, uid, *previousUid, cacheGid)) {
+ return error("Failed to chown " + path);
+ }
+ }
+ }
+
+ if (prepare_app_dir(path, targetMode, uid) ||
+ prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+ prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+ return error("Failed to prepare " + path);
+ }
+
+ // Consider restorecon over contents if label changed
+ if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+ return error("Failed to restorecon " + path);
+ }
+
+ return ok();
+}
+
binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
+ int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
+ int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -437,6 +559,14 @@
if (_aidl_return != nullptr) *_aidl_return = -1;
int32_t uid = multiuser_get_uid(userId, appId);
+
+ // If previousAppId < 0, we will use the existing app data owner as previousAppUid
+ // If previousAppId == 0, we use uid as previousUid (no data migration will happen)
+ // if previousAppId > 0, an app is upgrading and changing its app ID
+ int32_t previousUid = previousAppId > 0
+ ? (int32_t) multiuser_get_uid(userId, previousAppId)
+ : (previousAppId == 0 ? uid : -1);
+
int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
@@ -447,19 +577,13 @@
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
- bool existing = (access(path.c_str(), F_OK) == 0);
- if (prepare_app_dir(path, targetMode, uid) ||
- prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
- prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
- return error("Failed to prepare " + path);
+ auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ if (!status.isOk()) {
+ return status;
}
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
- return error("Failed to restorecon " + path);
+ if (previousUid != uid) {
+ chown_app_profile_dir(packageName, appId, userId);
}
// Remember inode numbers of cache directories so that we can clear
@@ -481,19 +605,10 @@
}
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
- bool existing = (access(path.c_str(), F_OK) == 0);
- if (prepare_app_dir(path, targetMode, uid) ||
- prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
- prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
- return error("Failed to prepare " + path);
- }
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
- return error("Failed to restorecon " + path);
+ auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ if (!status.isOk()) {
+ return status;
}
if (!prepare_app_profile_dir(packageName, appId, userId)) {
@@ -503,7 +618,6 @@
return ok();
}
-
binder::Status InstalldNativeService::createAppData(
const android::os::CreateAppDataArgs& args,
android::os::CreateAppDataResult* _aidl_return) {
@@ -512,7 +626,7 @@
int64_t ceDataInode = -1;
auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
- args.seInfo, args.targetSdkVersion, &ceDataInode);
+ args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode);
_aidl_return->ceDataInode = ceDataInode;
_aidl_return->exceptionCode = status.exceptionCode();
_aidl_return->exceptionMessage = status.exceptionMessage();
@@ -526,7 +640,7 @@
std::lock_guard<std::recursive_mutex> lock(mLock);
std::vector<android::os::CreateAppDataResult> results;
- for (auto arg : args) {
+ for (const auto &arg : args) {
android::os::CreateAppDataResult result;
createAppData(arg, &result);
results.push_back(result);
@@ -624,14 +738,11 @@
}
}
if (flags & FLAG_STORAGE_DE) {
- std::string suffix = "";
- bool only_cache = false;
+ std::string suffix;
if (flags & FLAG_CLEAR_CACHE_ONLY) {
suffix = CACHE_DIR_POSTFIX;
- only_cache = true;
} else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
suffix = CODE_CACHE_DIR_POSTFIX;
- only_cache = true;
}
auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix;
@@ -1226,7 +1337,7 @@
}
if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
- seInfo, targetSdkVersion, nullptr).isOk()) {
+ /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) {
res = error("Failed to create package target");
goto fail;
}
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ae257df..8cfda01 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -47,7 +47,8 @@
binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+ int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
+ int64_t* _aidl_return);
binder::Status createAppData(
const android::os::CreateAppDataArgs& args,
diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
index 96d7faa..d5e8ee5 100644
--- a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -23,6 +23,7 @@
int userId;
int flags;
int appId;
+ int previousAppId;
@utf8InCpp String seInfo;
int targetSdkVersion;
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index ea26955..a937436 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -287,6 +287,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_);
@@ -1257,6 +1258,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_));
@@ -1320,6 +1322,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode));
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index dc153c9..8270ae5 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: {
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/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 8fb4a37..9670d7b 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -207,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>
@@ -421,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/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 81aa551..6949c2c 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -104,6 +104,17 @@
return {};
}
+// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char
+static std::string SanitizeString(const String16& str) {
+ std::string sanitized{String8(str)};
+ for (auto& c : sanitized) {
+ if (!isprint(c)) {
+ c = '?';
+ }
+ }
+ return sanitized;
+}
+
bool AIBinder::associateClass(const AIBinder_Class* clazz) {
if (clazz == nullptr) return false;
@@ -118,7 +129,7 @@
if (descriptor != newDescriptor) {
if (getBinder()->isBinderAlive()) {
LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
- << "' but descriptor is actually '" << descriptor << "'.";
+ << "' but descriptor is actually '" << SanitizeString(descriptor) << "'.";
} else {
// b/155793159
LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
@@ -555,6 +566,10 @@
return ::android::IPCThreadState::self()->getCallingPid();
}
+bool AIBinder_isHandlingTransaction() {
+ return ::android::IPCThreadState::self()->getServingStackPointer() != nullptr;
+}
+
void AIBinder_incStrong(AIBinder* binder) {
if (binder == nullptr) {
return;
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 2b18a0a..67623a6 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -27,15 +27,67 @@
#pragma once
#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <optional>
#include <string>
+#include <type_traits>
#include <vector>
namespace ndk {
+namespace {
+template <typename Test, template <typename...> class Ref>
+struct is_specialization : std::false_type {};
+
+template <template <typename...> class Ref, typename... Args>
+struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
+
+template <typename Test, template <typename...> class Ref>
+static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
+
+// Get the first template type from a container, the T from MyClass<T, ...>.
+template <typename T>
+struct first_template_type {
+ using type = void;
+};
+
+template <template <typename...> class V, typename T, typename... Args>
+struct first_template_type<V<T, Args...>> {
+ using type = T;
+};
+
+template <typename T>
+using first_template_type_t = typename first_template_type<T>::type;
+
+// Tells if T represents NDK interface (shared_ptr<ICInterface-derived>)
+template <typename T>
+static inline constexpr bool is_interface_v = is_specialization_v<T, std::shared_ptr>&&
+ std::is_base_of_v<::ndk::ICInterface, first_template_type_t<T>>;
+
+// Tells if T represents NDK parcelable with readFromParcel/writeToParcel methods defined
+template <typename T, typename = void>
+struct is_parcelable : std::false_type {};
+
+template <typename T>
+struct is_parcelable<
+ T, std::void_t<decltype(std::declval<T>().readFromParcel(std::declval<const AParcel*>())),
+ decltype(std::declval<T>().writeToParcel(std::declval<AParcel*>()))>>
+ : std::true_type {};
+
+template <typename T>
+static inline constexpr bool is_parcelable_v = is_parcelable<T>::value;
+
+// Tells if T represents nullable NDK parcelable (optional<parcelable> or unique_ptr<parcelable>)
+template <typename T>
+static inline constexpr bool is_nullable_parcelable_v = is_parcelable_v<first_template_type_t<T>> &&
+ (is_specialization_v<T, std::optional> ||
+ is_specialization_v<T, std::unique_ptr>);
+
+} // namespace
+
/**
* This retrieves and allocates a vector to size 'length' and returns the underlying buffer.
*/
@@ -429,11 +481,19 @@
*/
template <typename P>
static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
+ if constexpr (is_interface_v<P>) {
+ if (!p) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return first_template_type_t<P>::writeToParcel(parcel, p);
+ } else {
+ static_assert(is_parcelable_v<P>);
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p.writeToParcel(parcel);
}
- return p.writeToParcel(parcel);
}
/**
@@ -441,85 +501,81 @@
*/
template <typename P>
static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
+ if constexpr (is_interface_v<P>) {
+ binder_status_t status = first_template_type_t<P>::readFromParcel(parcel, p);
+ if (status == STATUS_OK) {
+ if (!*p) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ }
return status;
+ } else {
+ static_assert(is_parcelable_v<P>);
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return p->readFromParcel(parcel);
}
- if (null == 0) {
- return STATUS_UNEXPECTED_NULL;
- }
- return p->readFromParcel(parcel);
}
/**
* Convenience API for writing a nullable parcelable.
*/
template <typename P>
-static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
- const std::optional<P>& p) {
- if (p == std::nullopt) {
- return AParcel_writeInt32(parcel, 0); // null
+static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, const P& p) {
+ if constexpr (is_interface_v<P>) {
+ return first_template_type_t<P>::writeToParcel(parcel, p);
+ } else {
+ static_assert(is_nullable_parcelable_v<P>);
+ if (!p) {
+ return AParcel_writeInt32(parcel, 0); // null
+ }
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p->writeToParcel(parcel);
}
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
- }
- return p->writeToParcel(parcel);
-}
-
-/**
- * Convenience API for writing a nullable parcelable.
- */
-template <typename P>
-static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
- const std::unique_ptr<P>& p) {
- if (!p) {
- return AParcel_writeInt32(parcel, 0); // null
- }
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
- }
- return p->writeToParcel(parcel);
}
/**
* Convenience API for reading a nullable parcelable.
*/
template <typename P>
-static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
- std::optional<P>* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
- return status;
+static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, P* p) {
+ if constexpr (is_interface_v<P>) {
+ return first_template_type_t<P>::readFromParcel(parcel, p);
+ } else if constexpr (is_specialization_v<P, std::optional>) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ *p = std::nullopt;
+ return STATUS_OK;
+ }
+ *p = std::optional<first_template_type_t<P>>(first_template_type_t<P>{});
+ return (*p)->readFromParcel(parcel);
+ } else {
+ static_assert(is_specialization_v<P, std::unique_ptr>);
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ p->reset();
+ return STATUS_OK;
+ }
+ *p = std::make_unique<first_template_type_t<P>>();
+ return (*p)->readFromParcel(parcel);
}
- if (null == 0) {
- *p = std::nullopt;
- return STATUS_OK;
- }
- *p = std::optional<P>(P{});
- return (*p)->readFromParcel(parcel);
-}
-
-/**
- * Convenience API for reading a nullable parcelable.
- */
-template <typename P>
-static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
- std::unique_ptr<P>* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
- return status;
- }
- if (null == 0) {
- p->reset();
- return STATUS_OK;
- }
- *p = std::make_unique<P>();
- return (*p)->readFromParcel(parcel);
}
/**
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 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..d63a8d0 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -145,6 +145,7 @@
global:
AIBinder_Class_disableInterfaceTokenHeader;
AIBinder_DeathRecipient_setOnUnlinked;
+ AIBinder_isHandlingTransaction;
AIBinder_setMinSchedulerPolicy; # llndk
AParcel_marshal;
AParcel_unmarshal;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index ecb044e..d323022 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -33,6 +33,27 @@
}
rust_library {
+ name: "libbinder_tokio_rs",
+ crate_name: "binder_tokio",
+ srcs: ["binder_tokio/lib.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ "libtokio",
+ ],
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ "com.android.virt",
+ ],
+}
+
+rust_library {
name: "libbinder_ndk_sys",
crate_name: "binder_ndk_sys",
srcs: [
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
new file mode 100644
index 0000000..64833b6
--- /dev/null
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! This crate lets you use the Tokio `spawn_blocking` pool with AIDL in async
+//! Rust code.
+//!
+//! This crate works by defining a type [`Tokio`], which you can use as the
+//! generic parameter in the async version of the trait generated by the AIDL
+//! compiler.
+//! ```text
+//! use binder_tokio::Tokio;
+//!
+//! binder::get_interface::<dyn SomeAsyncInterface<Tokio>>("...").
+//! ```
+//!
+//! [`Tokio`]: crate::Tokio
+
+use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
+use binder::{FromIBinder, StatusCode};
+use std::future::Future;
+
+/// Retrieve an existing service for a particular interface, sleeping for a few
+/// seconds if it doesn't yet exist.
+pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ let name = name.to_string();
+ let res = tokio::task::spawn_blocking(move || {
+ binder::public_api::get_interface::<T>(&name)
+ }).await;
+
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match res {
+ Ok(Ok(service)) => Ok(service),
+ Ok(Err(err)) => Err(err),
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR),
+ }
+}
+
+/// Retrieve an existing service for a particular interface, or start it if it
+/// is configured as a dynamic service and isn't yet started.
+pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ let name = name.to_string();
+ let res = tokio::task::spawn_blocking(move || {
+ binder::public_api::wait_for_interface::<T>(&name)
+ }).await;
+
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match res {
+ Ok(Ok(service)) => Ok(service),
+ Ok(Err(err)) => Err(err),
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR),
+ }
+}
+
+/// Use the Tokio `spawn_blocking` pool with AIDL.
+pub enum Tokio {}
+
+impl BinderAsyncPool for Tokio {
+ fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
+ where
+ F1: FnOnce() -> A,
+ F2: FnOnce(A) -> Fut,
+ Fut: Future<Output = Result<B, E>>,
+ F1: Send + 'static,
+ F2: Send + 'a,
+ Fut: Send + 'a,
+ A: Send + 'static,
+ B: Send + 'a,
+ E: From<crate::StatusCode>,
+ {
+ let handle = tokio::task::spawn_blocking(spawn_me);
+ Box::pin(async move {
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match handle.await {
+ Ok(res) => after_spawn(res).await,
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
+ }
+ })
+ }
+}
+
+
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index cc5dd06..4e048d7 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -713,12 +713,14 @@
$interface:path[$descriptor:expr] {
native: $native:ident($on_transact:path),
proxy: $proxy:ident,
+ $(async: $async_interface:ident,)?
}
} => {
$crate::declare_binder_interface! {
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ $(async: $async_interface,)?
stability: $crate::Stability::default(),
}
}
@@ -728,6 +730,7 @@
$interface:path[$descriptor:expr] {
native: $native:ident($on_transact:path),
proxy: $proxy:ident,
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -735,6 +738,7 @@
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -746,6 +750,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
}
} => {
$crate::declare_binder_interface! {
@@ -754,6 +759,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $crate::Stability::default(),
}
}
@@ -765,6 +771,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -776,6 +783,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -791,6 +799,8 @@
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $( async: $async_interface:ident, )?
+
stability: $stability:expr,
}
} => {
@@ -924,7 +934,7 @@
}
}
- impl std::fmt::Debug for dyn $interface {
+ impl std::fmt::Debug for dyn $interface + '_ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(stringify!($interface))
}
@@ -938,6 +948,73 @@
.expect(concat!("Error cloning interface ", stringify!($interface)))
}
}
+
+ $(
+ // Async interface trait implementations.
+ impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
+ fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
+ use $crate::AssociateClass;
+
+ let existing_class = ibinder.get_class();
+ if let Some(class) = existing_class {
+ if class != <$native as $crate::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ {
+ // The binder object's descriptor string matches what we
+ // expect. We still need to treat this local or already
+ // associated object as remote, because we can't cast it
+ // into a Rust service object without a matching class
+ // pointer.
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ }
+ }
+
+ if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ let service: $crate::Result<$crate::Binder<$native>> =
+ std::convert::TryFrom::try_from(ibinder.clone());
+ if let Ok(service) = service {
+ // We were able to associate with our expected class and
+ // the service is local.
+ todo!()
+ //return Ok($crate::Strong::new(Box::new(service)));
+ } else {
+ // Service is remote
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ }
+ }
+
+ Err($crate::StatusCode::BAD_TYPE.into())
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
+ fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ let binder = $crate::Interface::as_binder(self);
+ parcel.write(&binder)
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ parcel.write(&this.map($crate::Interface::as_binder))
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.pad(stringify!($async_interface))
+ }
+ }
+
+ /// Convert a &dyn $async_interface to Strong<dyn $async_interface>
+ impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> {
+ type Owned = $crate::Strong<dyn $async_interface<P>>;
+ fn to_owned(&self) -> Self::Owned {
+ self.as_binder().into_interface()
+ .expect(concat!("Error cloning interface ", stringify!($async_interface)))
+ }
+ }
+ )?
};
}
diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs
new file mode 100644
index 0000000..214c0b5
--- /dev/null
+++ b/libs/binder/rust/src/binder_async.rs
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use std::future::Future;
+use std::pin::Pin;
+
+/// A type alias for a pinned, boxed future that lets you write shorter code without littering it
+/// with Pin and Send bounds.
+pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
+
+/// A thread pool for running binder transactions.
+pub trait BinderAsyncPool {
+ /// This function should conceptually behave like this:
+ ///
+ /// ```text
+ /// let result = spawn_thread(|| spawn_me()).await;
+ /// return after_spawn(result).await;
+ /// ```
+ ///
+ /// If the spawning fails for some reason, the method may also skip the `after_spawn` closure
+ /// and immediately return an error.
+ ///
+ /// The only difference between different implementations should be which
+ /// `spawn_thread` method is used. For Tokio, it would be `tokio::task::spawn_blocking`.
+ ///
+ /// This method has the design it has because the only way to define a trait that
+ /// allows the return type of the spawn to be chosen by the caller is to return a
+ /// boxed `Future` trait object, and including `after_spawn` in the trait function
+ /// allows the caller to avoid double-boxing if they want to do anything to the value
+ /// returned from the spawned thread.
+ fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
+ where
+ F1: FnOnce() -> A,
+ F2: FnOnce(A) -> Fut,
+ Fut: Future<Output = Result<B, E>>,
+ F1: Send + 'static,
+ F2: Send + 'a,
+ Fut: Send + 'a,
+ A: Send + 'static,
+ B: Send + 'a,
+ E: From<crate::StatusCode>;
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 81b620e..2ac2d2f 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -98,6 +98,7 @@
#[macro_use]
mod binder;
+mod binder_async;
mod error;
mod native;
mod state;
@@ -111,6 +112,7 @@
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};
@@ -133,8 +135,9 @@
wait_for_interface,
};
pub use super::{
- BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder,
- Status, StatusCode, Strong, ThreadState, Weak, WpIBinder,
+ BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
+ Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
+ WpIBinder,
};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 9dba950..a0e1478 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -83,6 +83,19 @@
Self { ptr }
}
+ /// Convert the provided parcel to an owned parcel, or return `None` if it
+ /// is borrowed.
+ pub fn try_from(parcel: Parcel) -> Option<OwnedParcel> {
+ match &parcel {
+ Parcel::Owned(ptr) => {
+ let ptr = *ptr;
+ std::mem::forget(parcel);
+ Some(OwnedParcel { ptr })
+ }
+ Parcel::Borrowed(_) => None,
+ }
+ }
+
/// Create an owned reference to a parcel object from a raw pointer.
///
/// # Safety
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index ec00e1d..db9d8b0 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -702,6 +702,8 @@
}
}
+impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {}
+
impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
fn deserialize(parcel: &Parcel) -> Result<Self> {
let ibinder: SpIBinder = parcel.read()?;
@@ -716,6 +718,8 @@
}
}
+impl<T: FromIBinder + ?Sized> DeserializeArray for Strong<T> {}
+
// We need these to support Option<&T> for all T
impl<T: Serialize + ?Sized> Serialize for &T {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 0e05f10..0aef744 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -99,6 +99,17 @@
}
}
+ /// Determine whether the current thread is currently executing an incoming transaction.
+ ///
+ /// \return true if the current thread is currently executing an incoming transaction, and false
+ /// otherwise.
+ pub fn is_handling_transaction() -> bool {
+ unsafe {
+ // Safety: Safe FFI
+ sys::AIBinder_isHandlingTransaction()
+ }
+ }
+
/// This function makes the client's security context available to the
/// service calling this function. This can be used for access control.
/// It does not suffer from the TOCTOU issues of get_calling_pid.
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index ecc61f4..2d1175b 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -13,6 +13,8 @@
rustlibs: [
"libbinder_rs",
"libselinux_bindgen",
+ "libbinder_tokio_rs",
+ "libtokio",
],
shared_libs: [
"libselinux",
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 335e8d8..ebfe879 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -17,7 +17,7 @@
//! Rust Binder crate integration tests
use binder::declare_binder_interface;
-use binder::parcel::Parcel;
+use binder::parcel::{Parcel, OwnedParcel};
use binder::{
Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
FIRST_CALL_TRANSACTION,
@@ -154,12 +154,25 @@
fn get_selinux_context(&self) -> binder::Result<String>;
}
+/// Async trivial testing binder interface
+pub trait IATest<P>: Interface {
+ /// Returns a test string
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+
+ /// Return the arguments sent via dump
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+
+ /// Returns the caller's SELinux context
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+}
+
declare_binder_interface! {
ITest["android.os.ITest"] {
native: BnTest(on_transact),
proxy: BpTest {
x: i32 = 100
},
+ async: IATest,
}
}
@@ -201,6 +214,32 @@
}
}
+impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())).map(|p| OwnedParcel::try_from(p).unwrap()),
+ |reply| async move { reply?.into_parcel().read() }
+ )
+ }
+
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())).map(|p| OwnedParcel::try_from(p).unwrap()),
+ |reply| async move { reply?.into_parcel().read() }
+ )
+ }
+
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())).map(|p| OwnedParcel::try_from(p).unwrap()),
+ |reply| async move { reply?.into_parcel().read() }
+ )
+ }
+}
+
impl ITest for Binder<BnTest> {
fn test(&self) -> binder::Result<String> {
self.0.test()
@@ -215,6 +254,23 @@
}
}
+impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let res = self.0.test();
+ Box::pin(async move { res })
+ }
+
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ let res = self.0.get_dump_args();
+ Box::pin(async move { res })
+ }
+
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let res = self.0.get_selinux_context();
+ Box::pin(async move { res })
+ }
+}
+
/// Trivial testing binder interface
pub trait ITestSameDescriptor: Interface {}
@@ -255,7 +311,9 @@
SpIBinder, StatusCode, Strong,
};
- use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
+ use binder_tokio::Tokio;
+
+ use super::{BnTest, ITest, IATest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
pub struct ScopedServiceProcess(Child);
@@ -303,12 +361,47 @@
binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
Some(StatusCode::NAME_NOT_FOUND)
);
+ assert_eq!(
+ binder::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
// The service manager service isn't an ITest, so this must fail.
assert_eq!(
binder::get_interface::<dyn ITest>("manager").err(),
Some(StatusCode::BAD_TYPE)
);
+ assert_eq!(
+ binder::get_interface::<dyn IATest<Tokio>>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ }
+
+ #[tokio::test]
+ async fn check_services_async() {
+ let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
+ assert!(sm.is_binder_alive());
+ assert!(sm.ping_binder().is_ok());
+
+ assert!(binder::get_service("this_service_does_not_exist").is_none());
+ assert_eq!(
+ binder_tokio::get_interface::<dyn ITest>("this_service_does_not_exist").await.err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+ assert_eq!(
+ binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").await.err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+
+ // The service manager service isn't an ITest, so this must fail.
+ assert_eq!(
+ binder_tokio::get_interface::<dyn ITest>("manager").await.err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ assert_eq!(
+ binder_tokio::get_interface::<dyn IATest<Tokio>>("manager").await.err(),
+ Some(StatusCode::BAD_TYPE)
+ );
}
#[test]
@@ -323,6 +416,10 @@
binder::wait_for_interface::<dyn ITest>("manager").err(),
Some(StatusCode::BAD_TYPE)
);
+ assert_eq!(
+ binder::wait_for_interface::<dyn IATest<Tokio>>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
}
#[test]
@@ -334,6 +431,15 @@
assert_eq!(test_client.test().unwrap(), "trivial_client_test");
}
+ #[tokio::test]
+ async fn trivial_client_async() {
+ let service_name = "trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(test_client.test().await.unwrap(), "trivial_client_test");
+ }
+
#[test]
fn wait_for_trivial_client() {
let service_name = "wait_for_trivial_client_test";
@@ -343,23 +449,47 @@
assert_eq!(test_client.test().unwrap(), "wait_for_trivial_client_test");
}
+ #[tokio::test]
+ async fn wait_for_trivial_client_async() {
+ let service_name = "wait_for_trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::wait_for_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(test_client.test().await.unwrap(), "wait_for_trivial_client_test");
+ }
+
+ fn get_expected_selinux_context() -> &'static str {
+ unsafe {
+ let mut out_ptr = ptr::null_mut();
+ assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
+ assert!(!out_ptr.is_null());
+ CStr::from_ptr(out_ptr)
+ .to_str()
+ .expect("context was invalid UTF-8")
+ }
+ }
+
#[test]
fn get_selinux_context() {
let service_name = "get_selinux_context";
let _process = ScopedServiceProcess::new(service_name);
let test_client: Strong<dyn ITest> =
binder::get_interface(service_name).expect("Did not get manager binder service");
- let expected_context = unsafe {
- let mut out_ptr = ptr::null_mut();
- assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
- assert!(!out_ptr.is_null());
- CStr::from_ptr(out_ptr)
- };
assert_eq!(
test_client.get_selinux_context().unwrap(),
- expected_context
- .to_str()
- .expect("context was invalid UTF-8"),
+ get_expected_selinux_context()
+ );
+ }
+
+ #[tokio::test]
+ async fn get_selinux_context_async() {
+ let service_name = "get_selinux_context";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(
+ test_client.get_selinux_context().await.unwrap(),
+ get_expected_selinux_context()
);
}
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 155a25b..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),
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/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/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/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/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**) {