Merge "Only report PASS_THROUGH when VK_EXT_swapchain_colorspace enabled"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index ebb7891..34ea759 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1956,7 +1956,7 @@
join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
debuggable, boot_complete, for_restore, target_sdk_version,
enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
- compilation_reason);
+ background_job_compile, compilation_reason);
bool cancelled = false;
pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 51c4589..4221a3a 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -81,6 +81,7 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
bool use_jitzygote,
+ bool background_job_compile,
const char* compilation_reason) {
PrepareBootImageFlags(use_jitzygote);
@@ -92,7 +93,8 @@
debuggable, target_sdk_version, enable_hidden_api_checks,
generate_compact_dex, compilation_reason);
- PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore);
+ PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore,
+ background_job_compile);
const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
@@ -296,7 +298,8 @@
}
void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
- bool for_restore) {
+ bool for_restore,
+ bool background_job_compile) {
// CPU set
{
std::string cpu_set_format = "--cpu-set=%s";
@@ -306,7 +309,12 @@
"dalvik.vm.restore-dex2oat-cpu-set",
"dalvik.vm.dex2oat-cpu-set",
cpu_set_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))
+ : (background_job_compile
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.background-dex2oat-cpu-set",
+ "dalvik.vm.dex2oat-cpu-set",
+ cpu_set_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)))
: MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format);
AddArg(dex2oat_cpu_set_arg);
}
@@ -320,7 +328,12 @@
"dalvik.vm.restore-dex2oat-threads",
"dalvik.vm.dex2oat-threads",
threads_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
+ : (background_job_compile
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.background-dex2oat-threads",
+ "dalvik.vm.dex2oat-threads",
+ threads_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)))
: MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
AddArg(dex2oat_threads_arg);
}
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 559244f..c13e1f1 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -51,6 +51,7 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
bool use_jitzygote,
+ bool background_job_compile,
const char* compilation_reason);
void Exec(int exit_code);
@@ -76,7 +77,9 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
const char* compilation_reason);
- void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore);
+ void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
+ bool for_restore,
+ bool background_job_compile);
virtual std::string GetProperty(const std::string& key, const std::string& default_value);
virtual bool GetBoolProperty(const std::string& key, bool default_value);
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 2a8135a..304ba7b 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -115,6 +115,7 @@
bool enable_hidden_api_checks = false;
bool generate_compact_dex = true;
bool use_jitzygote = false;
+ bool background_job_compile = false;
const char* compilation_reason = nullptr;
};
@@ -259,6 +260,7 @@
args->enable_hidden_api_checks,
args->generate_compact_dex,
args->use_jitzygote,
+ args->background_job_compile,
args->compilation_reason);
runner.Exec(/*exit_code=*/ 0);
}
@@ -375,6 +377,30 @@
VerifyExpectedFlags();
}
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground) {
+ setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "1,3");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,3");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground_Backup) {
+ setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) {
setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2");
setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3");
@@ -481,6 +507,30 @@
VerifyExpectedFlags();
}
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground) {
+ setSystemProperty("dalvik.vm.background-dex2oat-threads", "2");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground_Backup) {
+ setSystemProperty("dalvik.vm.background-dex2oat-threads", "");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "3");
+ VerifyExpectedFlags();
+}
+
TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) {
setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4");
setSystemProperty("dalvik.vm.dex2oat-threads", "5");
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 2684f04..2ae61b9 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -612,7 +612,8 @@
}
void ServiceManager::tryStartService(const std::string& name) {
- ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service",
+ ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not "
+ "configured to be a lazy service, it may be stuck starting or still starting).",
name.c_str());
std::thread([=] {
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 fe5f35c..d6937c2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -270,14 +270,19 @@
std::string getDescription() const {
#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 30, *)) {
-#else
- if (__ANDROID_API__ >= 30) {
#endif
+
+#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 30
const char* cStr = AStatus_getDescription(get());
std::string ret = cStr;
AStatus_deleteDescription(cStr);
return ret;
+#endif
+
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
}
+#endif
+
binder_exception_t exception = getExceptionCode();
std::string desc = std::to_string(exception);
if (exception == EX_SERVICE_SPECIFIC) {
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index c1f2620..caee471 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -41,68 +41,34 @@
if (_status != STATUS_OK) return _status; \
} while (false)
+// AParcelableHolder has been introduced in 31.
+#if __ANDROID_API__ >= 31
class AParcelableHolder {
public:
AParcelableHolder() = delete;
explicit AParcelableHolder(parcelable_stability_t stability)
: mParcel(AParcel_create()), mStability(stability) {}
-#if __ANDROID_API__ >= 31
AParcelableHolder(const AParcelableHolder& other)
: mParcel(AParcel_create()), mStability(other.mStability) {
- // AParcelableHolder has been introduced in 31.
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
- AParcel_getDataSize(other.mParcel.get()));
- } else {
- syslog(LOG_ERR,
- "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
- }
+ AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
+ AParcel_getDataSize(other.mParcel.get()));
}
-#endif
AParcelableHolder(AParcelableHolder&& other) = default;
virtual ~AParcelableHolder() = default;
binder_status_t writeToParcel(AParcel* parcel) const {
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- int32_t size = AParcel_getDataSize(this->mParcel.get());
- RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
- } else {
- return STATUS_INVALID_OPERATION;
- }
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- int32_t size = AParcel_getDataSize(this->mParcel.get());
- RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
- } else {
- return STATUS_INVALID_OPERATION;
- }
+ int32_t size = AParcel_getDataSize(this->mParcel.get());
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
+ size = AParcel_getDataSize(this->mParcel.get());
+ RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
return STATUS_OK;
}
binder_status_t readFromParcel(const AParcel* parcel) {
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_reset(mParcel.get());
- } else {
- return STATUS_INVALID_OPERATION;
- }
+ AParcel_reset(mParcel.get());
parcelable_stability_t wireStability;
RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability));
@@ -123,15 +89,7 @@
return STATUS_BAD_VALUE;
}
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
- } else {
- status = STATUS_INVALID_OPERATION;
- }
+ status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
if (status != STATUS_OK) {
return status;
}
@@ -143,15 +101,7 @@
if (this->mStability > T::_aidl_stability) {
return STATUS_BAD_VALUE;
}
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_reset(mParcel.get());
- } else {
- return STATUS_INVALID_OPERATION;
- }
+ AParcel_reset(mParcel.get());
AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
p.writeToParcel(mParcel.get());
return STATUS_OK;
@@ -161,17 +111,9 @@
binder_status_t getParcelable(std::optional<T>* ret) const {
const std::string parcelableDesc(T::descriptor);
AParcel_setDataPosition(mParcel.get(), 0);
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- if (AParcel_getDataSize(mParcel.get()) == 0) {
- *ret = std::nullopt;
- return STATUS_OK;
- }
- } else {
- return STATUS_INVALID_OPERATION;
+ if (AParcel_getDataSize(mParcel.get()) == 0) {
+ *ret = std::nullopt;
+ return STATUS_OK;
}
std::string parcelableDescInParcel;
binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
@@ -188,18 +130,7 @@
return STATUS_OK;
}
- void reset() {
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_reset(mParcel.get());
- } else {
- syslog(LOG_ERR,
- "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
- }
- }
+ void reset() { AParcel_reset(mParcel.get()); }
inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
@@ -207,34 +138,23 @@
inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
-#if __ANDROID_API__ >= 31
inline AParcelableHolder& operator=(const AParcelableHolder& rhs) {
- // AParcelableHolder has been introduced in 31.
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- this->reset();
- if (this->mStability != rhs.mStability) {
- syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!",
- this->mStability, rhs.mStability);
- abort();
- }
- AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0,
- AParcel_getDataSize(rhs.mParcel.get()));
- } else {
- syslog(LOG_ERR,
- "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
+ this->reset();
+ if (this->mStability != rhs.mStability) {
+ syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!",
+ this->mStability, rhs.mStability);
+ abort();
}
+ AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0,
+ AParcel_getDataSize(rhs.mParcel.get()));
return *this;
}
-#endif
private:
mutable ndk::ScopedAParcel mParcel;
parcelable_stability_t mStability;
};
+#endif // __ANDROID_API__ >= 31
#undef RETURN_ON_FAILURE
} // namespace ndk
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index d7840ec..6a25db2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -136,8 +136,10 @@
template <typename _U>
static std::enable_if_t<
#ifdef HAS_NDK_INTERFACE
- std::is_base_of_v<::ndk::ICInterface, _U> ||
- std::is_same_v<::ndk::AParcelableHolder, _U>
+ std::is_base_of_v<::ndk::ICInterface, _U>
+#if __ANDROID_API__ >= 31
+ || std::is_same_v<::ndk::AParcelableHolder, _U>
+#endif
#else
std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> ||
std::is_same_v<os::ParcelFileDescriptor, _U> ||
diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
index 21271d2..c5c7719 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
@@ -22,7 +22,7 @@
mod read_utils;
-use crate::read_utils::get_read_funcs;
+use crate::read_utils::READ_FUNCS;
use binder::binder_impl::{
Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode,
};
@@ -34,18 +34,18 @@
use libfuzzer_sys::arbitrary::Arbitrary;
#[derive(Arbitrary, Debug)]
-enum ReadOperations {
+enum ReadOperation {
SetDataPosition { pos: i32 },
GetDataSize,
ReadParcelableHolder { is_vintf: bool },
- ReadBasicTypes { indexes: Vec<usize> },
+ ReadBasicTypes { instructions: Vec<usize> },
}
#[derive(Arbitrary, Debug)]
-enum Operations<'a> {
+enum Operation<'a> {
Transact { code: u32, flag: u32, data: &'a [u8] },
Append { start: i32, len: i32, data1: &'a [u8], data2: &'a [u8], append_all: bool },
- Read { indexes: Vec<ReadOperations>, data: &'a [u8] },
+ Read { read_operations: Vec<ReadOperation>, data: &'a [u8] },
}
/// Interface to fuzz transact with random parcel
@@ -102,13 +102,12 @@
};
}
-fn do_read_fuzz(read_operations: Vec<ReadOperations>, data: &[u8]) {
- let read_funcs = get_read_funcs();
+fn do_read_fuzz(read_operations: Vec<ReadOperation>, data: &[u8]) {
let parcel = create_random_parcel(data);
for operation in read_operations {
match operation {
- ReadOperations::SetDataPosition { pos } => {
+ ReadOperation::SetDataPosition { pos } => {
unsafe {
// Safety: Safe if pos is less than current size of the parcel.
// It relies on C++ code for bound checks
@@ -119,12 +118,12 @@
}
}
- ReadOperations::GetDataSize => {
+ ReadOperation::GetDataSize => {
let data_size = parcel.get_data_size();
println!("data size from parcel: {:?}", data_size);
}
- ReadOperations::ReadParcelableHolder { is_vintf } => {
+ ReadOperation::ReadParcelableHolder { is_vintf } => {
let stability = if is_vintf { Stability::Vintf } else { Stability::Local };
let mut holder: ParcelableHolder = ParcelableHolder::new(stability);
match holder.read_from_parcel(parcel.borrowed_ref()) {
@@ -135,30 +134,28 @@
}
}
- ReadOperations::ReadBasicTypes { indexes } => {
- for index in indexes.iter() {
- let read_index = index % read_funcs.len();
- read_funcs[read_index](parcel.borrowed_ref());
+ ReadOperation::ReadBasicTypes { instructions } => {
+ for instruction in instructions.iter() {
+ let read_index = instruction % READ_FUNCS.len();
+ READ_FUNCS[read_index](parcel.borrowed_ref());
}
}
}
}
}
-fuzz_target!(|operations: Vec<Operations>| {
- for operation in operations {
- match operation {
- Operations::Transact { code, flag, data } => {
- do_transact(code, data, flag);
- }
+fuzz_target!(|operation: Operation| {
+ match operation {
+ Operation::Transact { code, flag, data } => {
+ do_transact(code, data, flag);
+ }
- Operations::Append { start, len, data1, data2, append_all } => {
- do_append_fuzz(start, len, data1, data2, append_all);
- }
+ Operation::Append { start, len, data1, data2, append_all } => {
+ do_append_fuzz(start, len, data1, data2, append_all);
+ }
- Operations::Read { indexes, data } => {
- do_read_fuzz(indexes, data);
- }
+ Operation::Read { read_operations, data } => {
+ do_read_fuzz(read_operations, data);
}
}
});
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
index 9b06013..d2bfde1 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -22,7 +22,7 @@
macro_rules! read_parcel_interface {
($data_type:ty) => {
- |parcel| {
+ |parcel: &BorrowedParcel<'_>| {
let _res = parcel.read::<$data_type>();
}
};
@@ -57,81 +57,77 @@
binder::impl_deserialize_for_parcelable!(SomeParcelable);
-pub fn get_read_funcs() -> Vec<Box<dyn Fn(&BorrowedParcel<'_>)>> {
- let read_funcs: Vec<Box<dyn Fn(&BorrowedParcel<'_>)>> = vec![
- //read basic types
- Box::new(read_parcel_interface!(bool)),
- Box::new(read_parcel_interface!(i8)),
- Box::new(read_parcel_interface!(i32)),
- Box::new(read_parcel_interface!(i64)),
- Box::new(read_parcel_interface!(f32)),
- Box::new(read_parcel_interface!(f64)),
- Box::new(read_parcel_interface!(u16)),
- Box::new(read_parcel_interface!(u32)),
- Box::new(read_parcel_interface!(u64)),
- Box::new(read_parcel_interface!(String)),
- //read vec of basic types
- Box::new(read_parcel_interface!(Vec<i8>)),
- Box::new(read_parcel_interface!(Vec<i32>)),
- Box::new(read_parcel_interface!(Vec<i64>)),
- Box::new(read_parcel_interface!(Vec<f32>)),
- Box::new(read_parcel_interface!(Vec<f64>)),
- Box::new(read_parcel_interface!(Vec<u16>)),
- Box::new(read_parcel_interface!(Vec<u32>)),
- Box::new(read_parcel_interface!(Vec<u64>)),
- Box::new(read_parcel_interface!(Vec<String>)),
- Box::new(read_parcel_interface!(Option<Vec<i8>>)),
- Box::new(read_parcel_interface!(Option<Vec<i32>>)),
- Box::new(read_parcel_interface!(Option<Vec<i64>>)),
- Box::new(read_parcel_interface!(Option<Vec<f32>>)),
- Box::new(read_parcel_interface!(Option<Vec<f64>>)),
- Box::new(read_parcel_interface!(Option<Vec<u16>>)),
- Box::new(read_parcel_interface!(Option<Vec<u32>>)),
- Box::new(read_parcel_interface!(Option<Vec<u64>>)),
- Box::new(read_parcel_interface!(Option<Vec<String>>)),
- Box::new(read_parcel_interface!(ParcelFileDescriptor)),
- Box::new(read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>)),
- Box::new(read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>)),
- Box::new(read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>)),
- Box::new(read_parcel_interface!(SpIBinder)),
- Box::new(read_parcel_interface!(Vec<Option<SpIBinder>>)),
- Box::new(read_parcel_interface!(Option<Vec<SpIBinder>>)),
- Box::new(read_parcel_interface!(Option<Vec<Option<SpIBinder>>>)),
- Box::new(read_parcel_interface!(SomeParcelable)),
- Box::new(read_parcel_interface!(Vec<Option<SomeParcelable>>)),
- Box::new(read_parcel_interface!(Option<Vec<SomeParcelable>>)),
- Box::new(read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>)),
- // Fuzz read_from_parcel for AIDL generated parcelables
- Box::new(|parcel| {
- let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default();
- match empty_parcelable.read_from_parcel(parcel) {
- Ok(result) => result,
- Err(e) => {
- println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e)
- }
+pub const READ_FUNCS: &[fn(&BorrowedParcel<'_>)] = &[
+ //read basic types
+ read_parcel_interface!(bool),
+ read_parcel_interface!(i8),
+ read_parcel_interface!(i32),
+ read_parcel_interface!(i64),
+ read_parcel_interface!(f32),
+ read_parcel_interface!(f64),
+ read_parcel_interface!(u16),
+ read_parcel_interface!(u32),
+ read_parcel_interface!(u64),
+ read_parcel_interface!(String),
+ //read vec of basic types
+ read_parcel_interface!(Vec<i8>),
+ read_parcel_interface!(Vec<i32>),
+ read_parcel_interface!(Vec<i64>),
+ read_parcel_interface!(Vec<f32>),
+ read_parcel_interface!(Vec<f64>),
+ read_parcel_interface!(Vec<u16>),
+ read_parcel_interface!(Vec<u32>),
+ read_parcel_interface!(Vec<u64>),
+ read_parcel_interface!(Vec<String>),
+ read_parcel_interface!(Option<Vec<i8>>),
+ read_parcel_interface!(Option<Vec<i32>>),
+ read_parcel_interface!(Option<Vec<i64>>),
+ read_parcel_interface!(Option<Vec<f32>>),
+ read_parcel_interface!(Option<Vec<f64>>),
+ read_parcel_interface!(Option<Vec<u16>>),
+ read_parcel_interface!(Option<Vec<u32>>),
+ read_parcel_interface!(Option<Vec<u64>>),
+ read_parcel_interface!(Option<Vec<String>>),
+ read_parcel_interface!(ParcelFileDescriptor),
+ read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>),
+ read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>),
+ read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>),
+ read_parcel_interface!(SpIBinder),
+ read_parcel_interface!(Vec<Option<SpIBinder>>),
+ read_parcel_interface!(Option<Vec<SpIBinder>>),
+ read_parcel_interface!(Option<Vec<Option<SpIBinder>>>),
+ read_parcel_interface!(SomeParcelable),
+ read_parcel_interface!(Vec<Option<SomeParcelable>>),
+ read_parcel_interface!(Option<Vec<SomeParcelable>>),
+ read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>),
+ // Fuzz read_from_parcel for AIDL generated parcelables
+ |parcel| {
+ let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default();
+ match empty_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => {
+ println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e)
}
- }),
- Box::new(|parcel| {
- let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default();
- match single_parcelable.read_from_parcel(parcel) {
- Ok(result) => result,
- Err(e) => println!(
- "SingleDataParcelable: error occurred while reading from a parcel: {:?}",
- e
- ),
- }
- }),
- Box::new(|parcel| {
- let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default();
- match generic_parcelable.read_from_parcel(parcel) {
- Ok(result) => result,
- Err(e) => println!(
- "GenericDataParcelable: error occurred while reading from a parcel: {:?}",
- e
- ),
- }
- }),
- ];
-
- read_funcs
-}
+ }
+ },
+ |parcel| {
+ let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default();
+ match single_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => println!(
+ "SingleDataParcelable: error occurred while reading from a parcel: {:?}",
+ e
+ ),
+ }
+ },
+ |parcel| {
+ let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default();
+ match generic_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => println!(
+ "GenericDataParcelable: error occurred while reading from a parcel: {:?}",
+ e
+ ),
+ }
+ },
+];
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 92d132f..03e4a23 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -232,6 +232,7 @@
srcs: [
"binderRpcTest.cpp",
"binderRpcTestCommon.cpp",
+ "binderRpcUniversalTests.cpp",
],
test_suites: ["general-tests"],
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 7294305..004dea3 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -15,7 +15,6 @@
*/
#include <android-base/stringprintf.h>
-#include <gtest/gtest.h>
#include <chrono>
#include <cstdlib>
@@ -29,6 +28,7 @@
#include <sys/socket.h>
#include "binderRpcTestCommon.h"
+#include "binderRpcTestFixture.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -44,37 +44,6 @@
constexpr bool kEnableSharedLibs = true;
#endif
-static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
- RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
-
-TEST(BinderRpcParcel, EntireParcelFormatted) {
- Parcel p;
- p.writeInt32(3);
-
- EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "format must be set before data is written");
-}
-
-TEST(BinderRpc, CannotUseNextWireVersion) {
- auto session = RpcSession::make();
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
-}
-
-TEST(BinderRpc, CanUseExperimentalWireVersion) {
- auto session = RpcSession::make();
- EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
-}
-
-using android::binder::Status;
-
-#define EXPECT_OK(status) \
- do { \
- Status stat = (status); \
- EXPECT_TRUE(stat.isOk()) << stat; \
- } while (false)
-
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
@@ -92,7 +61,15 @@
class Process {
public:
- Process(Process&&) = default;
+ Process(Process&& other)
+ : mCustomExitStatusCheck(std::move(other.mCustomExitStatusCheck)),
+ mReadEnd(std::move(other.mReadEnd)),
+ mWriteEnd(std::move(other.mWriteEnd)) {
+ // The default move constructor doesn't clear mPid after moving it,
+ // which we need to do because the destructor checks for mPid!=0
+ mPid = other.mPid;
+ other.mPid = 0;
+ }
Process(const std::function<void(android::base::borrowed_fd /* writeEnd */,
android::base::borrowed_fd /* readEnd */)>& f) {
android::base::unique_fd childWriteEnd;
@@ -152,21 +129,17 @@
return vsockPort++;
}
-struct ProcessSession {
+// Destructors need to be defined, even if pure virtual
+ProcessSession::~ProcessSession() {}
+
+class LinuxProcessSession : public ProcessSession {
+public:
// reference to process hosting a socket server
Process host;
- struct SessionInfo {
- sp<RpcSession> session;
- sp<IBinder> root;
- };
-
- // client session objects associated with other process
- // each one represents a separate session
- std::vector<SessionInfo> sessions;
-
- ProcessSession(ProcessSession&&) = default;
- ~ProcessSession() {
+ LinuxProcessSession(LinuxProcessSession&&) = default;
+ LinuxProcessSession(Process&& host) : host(std::move(host)) {}
+ ~LinuxProcessSession() override {
for (auto& session : sessions) {
session.root = nullptr;
}
@@ -197,46 +170,12 @@
}
}
}
-};
-// Process session where the process hosts IBinderRpcTest, the server used
-// for most testing here
-struct BinderRpcTestProcessSession {
- ProcessSession proc;
-
- // pre-fetched root object (for first session)
- sp<IBinder> rootBinder;
-
- // pre-casted root object (for first session)
- sp<IBinderRpcTest> rootIface;
-
- // whether session should be invalidated by end of run
- bool expectAlreadyShutdown = false;
-
- BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
- ~BinderRpcTestProcessSession() {
- if (!expectAlreadyShutdown) {
- EXPECT_NE(nullptr, rootIface);
- if (rootIface == nullptr) return;
-
- std::vector<int32_t> remoteCounts;
- // calling over any sessions counts across all sessions
- EXPECT_OK(rootIface->countBinders(&remoteCounts));
- EXPECT_EQ(remoteCounts.size(), proc.sessions.size());
- for (auto remoteCount : remoteCounts) {
- EXPECT_EQ(remoteCount, 1);
- }
-
- // even though it is on another thread, shutdown races with
- // the transaction reply being written
- if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
- EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
- }
- }
-
- rootIface = nullptr;
- rootBinder = nullptr;
+ void setCustomExitStatusCheck(std::function<void(int wstatus)> f) override {
+ host.setCustomExitStatusCheck(std::move(f));
}
+
+ void terminate() override { host.terminate(); }
};
static base::unique_fd connectTo(const RpcSocketAddress& addr) {
@@ -273,552 +212,130 @@
return std::move(sockClient);
}
-using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd,
- android::base::borrowed_fd readEnd);
+std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
+ auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
+ std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
+ if (singleThreaded) {
+ ret += "_single_threaded";
+ }
+ if (noKernel) {
+ ret += "_no_kernel";
+ }
+ return ret;
+}
-class BinderRpc : public ::testing::TestWithParam<
- std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
-public:
- SocketType socketType() const { return std::get<0>(GetParam()); }
- RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
- uint32_t clientVersion() const { return std::get<2>(GetParam()); }
- uint32_t serverVersion() const { return std::get<3>(GetParam()); }
- bool serverSingleThreaded() const { return std::get<4>(GetParam()); }
- bool noKernel() const { return std::get<5>(GetParam()); }
+// This creates a new process serving an interface on a certain number of
+// threads.
+std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
+ const BinderRpcOptions& options) {
+ CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
- bool clientOrServerSingleThreaded() const {
- return !kEnableRpcThreads || serverSingleThreaded();
+ SocketType socketType = std::get<0>(GetParam());
+ RpcSecurity rpcSecurity = std::get<1>(GetParam());
+ uint32_t clientVersion = std::get<2>(GetParam());
+ uint32_t serverVersion = std::get<3>(GetParam());
+ bool singleThreaded = std::get<4>(GetParam());
+ bool noKernel = std::get<5>(GetParam());
+
+ std::string path = android::base::GetExecutableDirectory();
+ auto servicePath = android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
+ singleThreaded ? "_single_threaded" : "",
+ noKernel ? "_no_kernel" : "");
+
+ base::unique_fd bootstrapClientFd, bootstrapServerFd;
+ // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec.
+ // This is because we cannot pass ParcelFileDescriptor over a pipe.
+ if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
}
- // Whether the test params support sending FDs in parcels.
- bool supportsFdTransport() const {
- return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
- (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
- socketType() == SocketType::UNIX_BOOTSTRAP);
+ auto ret = std::make_unique<LinuxProcessSession>(
+ Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) {
+ auto writeFd = std::to_string(writeEnd.get());
+ auto readFd = std::to_string(readEnd.get());
+ execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
+ NULL);
+ }));
+
+ BinderRpcTestServerConfig serverConfig;
+ serverConfig.numThreads = options.numThreads;
+ serverConfig.socketType = static_cast<int32_t>(socketType);
+ serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity);
+ serverConfig.serverVersion = serverVersion;
+ serverConfig.vsockPort = allocateVsockPort();
+ serverConfig.addr = allocateSocketAddress();
+ serverConfig.unixBootstrapFd = bootstrapServerFd.get();
+ for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
+ serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
+ static_cast<int32_t>(mode));
+ }
+ writeToFd(ret->host.writeEnd(), serverConfig);
+
+ std::vector<sp<RpcSession>> sessions;
+ auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+ for (size_t i = 0; i < options.numSessions; i++) {
+ sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
}
- void SetUp() override {
- if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) {
- GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport";
- }
+ auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
+ BinderRpcTestClientInfo clientInfo;
+ for (const auto& session : sessions) {
+ auto& parcelableCert = clientInfo.certs.emplace_back();
+ parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
+ }
+ writeToFd(ret->host.writeEnd(), clientInfo);
+
+ CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
+ if (socketType == SocketType::INET) {
+ CHECK_NE(0, serverInfo.port);
}
- static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
- auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
- std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
- if (singleThreaded) {
- ret += "_single_threaded";
- }
- if (noKernel) {
- ret += "_no_kernel";
- }
- return ret;
+ if (rpcSecurity == RpcSecurity::TLS) {
+ const auto& serverCert = serverInfo.cert.data;
+ CHECK_EQ(OK,
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert));
}
- // This creates a new process serving an interface on a certain number of
- // threads.
- ProcessSession createRpcTestSocketServerProcessEtc(const BinderRpcOptions& options) {
- CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
+ status_t status;
- SocketType socketType = std::get<0>(GetParam());
- RpcSecurity rpcSecurity = std::get<1>(GetParam());
- uint32_t clientVersion = std::get<2>(GetParam());
- uint32_t serverVersion = std::get<3>(GetParam());
- bool singleThreaded = std::get<4>(GetParam());
- bool noKernel = std::get<5>(GetParam());
+ for (const auto& session : sessions) {
+ CHECK(session->setProtocolVersion(clientVersion));
+ session->setMaxIncomingThreads(options.numIncomingConnections);
+ session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
- std::string path = android::base::GetExecutableDirectory();
- auto servicePath =
- android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
- singleThreaded ? "_single_threaded" : "",
- noKernel ? "_no_kernel" : "");
-
- base::unique_fd bootstrapClientFd, bootstrapServerFd;
- // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec.
- // This is because we cannot pass ParcelFileDescriptor over a pipe.
- if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) {
- int savedErrno = errno;
- LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
- }
-
- auto ret = ProcessSession{
- .host = Process([=](android::base::borrowed_fd writeEnd,
- android::base::borrowed_fd readEnd) {
- auto writeFd = std::to_string(writeEnd.get());
- auto readFd = std::to_string(readEnd.get());
- execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
- NULL);
- }),
- };
-
- BinderRpcTestServerConfig serverConfig;
- serverConfig.numThreads = options.numThreads;
- serverConfig.socketType = static_cast<int32_t>(socketType);
- serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity);
- serverConfig.serverVersion = serverVersion;
- serverConfig.vsockPort = allocateVsockPort();
- serverConfig.addr = allocateSocketAddress();
- serverConfig.unixBootstrapFd = bootstrapServerFd.get();
- for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
- serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
- static_cast<int32_t>(mode));
- }
- writeToFd(ret.host.writeEnd(), serverConfig);
-
- std::vector<sp<RpcSession>> sessions;
- auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
- for (size_t i = 0; i < options.numSessions; i++) {
- sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
- }
-
- auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd());
- BinderRpcTestClientInfo clientInfo;
- for (const auto& session : sessions) {
- auto& parcelableCert = clientInfo.certs.emplace_back();
- parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
- }
- writeToFd(ret.host.writeEnd(), clientInfo);
-
- CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
- if (socketType == SocketType::INET) {
- CHECK_NE(0, serverInfo.port);
- }
-
- if (rpcSecurity == RpcSecurity::TLS) {
- const auto& serverCert = serverInfo.cert.data;
- CHECK_EQ(OK,
- certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
- serverCert));
- }
-
- status_t status;
-
- for (const auto& session : sessions) {
- CHECK(session->setProtocolVersion(clientVersion));
- session->setMaxIncomingThreads(options.numIncomingConnections);
- session->setMaxOutgoingThreads(options.numOutgoingConnections);
- session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
-
- switch (socketType) {
- case SocketType::PRECONNECTED:
- status = session->setupPreconnectedClient({}, [=]() {
- return connectTo(UnixSocketAddress(serverConfig.addr.c_str()));
- });
- break;
- case SocketType::UNIX:
- status = session->setupUnixDomainClient(serverConfig.addr.c_str());
- break;
- case SocketType::UNIX_BOOTSTRAP:
- status = session->setupUnixDomainSocketBootstrapClient(
- base::unique_fd(dup(bootstrapClientFd.get())));
- break;
- case SocketType::VSOCK:
- status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
- break;
- case SocketType::INET:
- status = session->setupInetClient("127.0.0.1", serverInfo.port);
- break;
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- }
- if (options.allowConnectFailure && status != OK) {
- ret.sessions.clear();
+ switch (socketType) {
+ case SocketType::PRECONNECTED:
+ status = session->setupPreconnectedClient({}, [=]() {
+ return connectTo(UnixSocketAddress(serverConfig.addr.c_str()));
+ });
break;
- }
- CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
- ret.sessions.push_back({session, session->getRootObject()});
+ case SocketType::UNIX:
+ status = session->setupUnixDomainClient(serverConfig.addr.c_str());
+ break;
+ case SocketType::UNIX_BOOTSTRAP:
+ status = session->setupUnixDomainSocketBootstrapClient(
+ base::unique_fd(dup(bootstrapClientFd.get())));
+ break;
+ case SocketType::VSOCK:
+ status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
+ break;
+ case SocketType::INET:
+ status = session->setupInetClient("127.0.0.1", serverInfo.port);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
}
- return ret;
+ if (options.allowConnectFailure && status != OK) {
+ ret->sessions.clear();
+ break;
+ }
+ CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
+ ret->sessions.push_back({session, session->getRootObject()});
}
-
- BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
- BinderRpcTestProcessSession ret{
- .proc = createRpcTestSocketServerProcessEtc(options),
- };
-
- ret.rootBinder = ret.proc.sessions.empty() ? nullptr : ret.proc.sessions.at(0).root;
- ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
-
- return ret;
- }
-
- void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs = 500);
-};
-
-TEST_P(BinderRpc, Ping) {
- auto proc = createRpcTestSocketServerProcess({});
- ASSERT_NE(proc.rootBinder, nullptr);
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-}
-
-TEST_P(BinderRpc, GetInterfaceDescriptor) {
- auto proc = createRpcTestSocketServerProcess({});
- ASSERT_NE(proc.rootBinder, nullptr);
- EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
-}
-
-TEST_P(BinderRpc, MultipleSessions) {
- if (serverSingleThreaded()) {
- // Tests with multiple sessions require a multi-threaded service,
- // but work fine on a single-threaded client
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
- for (auto session : proc.proc.sessions) {
- ASSERT_NE(nullptr, session.root);
- EXPECT_EQ(OK, session.root->pingBinder());
- }
-}
-
-TEST_P(BinderRpc, SeparateRootObject) {
- if (serverSingleThreaded()) {
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- SocketType type = std::get<0>(GetParam());
- if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
- type == SocketType::UNIX_BOOTSTRAP) {
- // we can't get port numbers for unix sockets
- return;
- }
-
- auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
-
- int port1 = 0;
- EXPECT_OK(proc.rootIface->getClientPort(&port1));
-
- sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root);
- int port2;
- EXPECT_OK(rootIface2->getClientPort(&port2));
-
- // we should have a different IBinderRpcTest object created for each
- // session, because we use setPerSessionRootObject
- EXPECT_NE(port1, port2);
-}
-
-TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
- auto proc = createRpcTestSocketServerProcess({});
- Parcel data;
- Parcel reply;
- EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
-}
-
-TEST_P(BinderRpc, AppendSeparateFormats) {
- auto proc1 = createRpcTestSocketServerProcess({});
- auto proc2 = createRpcTestSocketServerProcess({});
-
- Parcel pRaw;
-
- Parcel p1;
- p1.markForBinder(proc1.rootBinder);
- p1.writeInt32(3);
-
- EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
- EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
-
- Parcel p2;
- p2.markForBinder(proc2.rootBinder);
- p2.writeInt32(7);
-
- EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
- EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
-}
-
-TEST_P(BinderRpc, UnknownTransaction) {
- auto proc = createRpcTestSocketServerProcess({});
- Parcel data;
- data.markForBinder(proc.rootBinder);
- Parcel reply;
- EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
-}
-
-TEST_P(BinderRpc, SendSomethingOneway) {
- auto proc = createRpcTestSocketServerProcess({});
- EXPECT_OK(proc.rootIface->sendString("asdf"));
-}
-
-TEST_P(BinderRpc, SendAndGetResultBack) {
- auto proc = createRpcTestSocketServerProcess({});
- std::string doubled;
- EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
- EXPECT_EQ("cool cool ", doubled);
-}
-
-TEST_P(BinderRpc, SendAndGetResultBackBig) {
- auto proc = createRpcTestSocketServerProcess({});
- std::string single = std::string(1024, 'a');
- std::string doubled;
- EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
- EXPECT_EQ(single + single, doubled);
-}
-
-TEST_P(BinderRpc, InvalidNullBinderReturn) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
-}
-
-TEST_P(BinderRpc, CallMeBack) {
- auto proc = createRpcTestSocketServerProcess({});
-
- int32_t pingResult;
- EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
- EXPECT_EQ(OK, pingResult);
-
- EXPECT_EQ(0, MyBinderRpcSession::gNum);
-}
-
-TEST_P(BinderRpc, RepeatBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> inBinder = new MyBinderRpcSession("foo");
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
- EXPECT_EQ(inBinder, outBinder);
-
- wp<IBinder> weak = inBinder;
- inBinder = nullptr;
- outBinder = nullptr;
-
- // Force reading a reply, to process any pending dec refs from the other
- // process (the other process will process dec refs there before processing
- // the ping here).
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
- EXPECT_EQ(nullptr, weak.promote());
-
- EXPECT_EQ(0, MyBinderRpcSession::gNum);
-}
-
-TEST_P(BinderRpc, RepeatTheirBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
-
- sp<IBinder> inBinder = IInterface::asBinder(session);
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
- EXPECT_EQ(inBinder, outBinder);
-
- wp<IBinder> weak = inBinder;
- session = nullptr;
- inBinder = nullptr;
- outBinder = nullptr;
-
- // Force reading a reply, to process any pending dec refs from the other
- // process (the other process will process dec refs there before processing
- // the ping here).
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
- EXPECT_EQ(nullptr, weak.promote());
-}
-
-TEST_P(BinderRpc, RepeatBinderNull) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
- EXPECT_EQ(nullptr, outBinder);
-}
-
-TEST_P(BinderRpc, HoldBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- IBinder* ptr = nullptr;
- {
- sp<IBinder> binder = new BBinder();
- ptr = binder.get();
- EXPECT_OK(proc.rootIface->holdBinder(binder));
- }
-
- sp<IBinder> held;
- EXPECT_OK(proc.rootIface->getHeldBinder(&held));
-
- EXPECT_EQ(held.get(), ptr);
-
- // stop holding binder, because we test to make sure references are cleaned
- // up
- EXPECT_OK(proc.rootIface->holdBinder(nullptr));
- // and flush ref counts
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-}
-
-// START TESTS FOR LIMITATIONS OF SOCKET BINDER
-// These are behavioral differences form regular binder, where certain usecases
-// aren't supported.
-
-TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
- auto proc1 = createRpcTestSocketServerProcess({});
- auto proc2 = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
-}
-
-TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
- if (serverSingleThreaded()) {
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder)
- .transactionError());
-}
-
-TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
- if (!kEnableKernelIpc || noKernel()) {
- GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
- "at build time.";
- }
-
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
-}
-
-TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
- if (!kEnableKernelIpc || noKernel()) {
- GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
- "at build time.";
- }
-
- auto proc = createRpcTestSocketServerProcess({});
-
- // for historical reasons, IServiceManager interface only returns the
- // exception code
- EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
- defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
-}
-
-// END TESTS FOR LIMITATIONS OF SOCKET BINDER
-
-TEST_P(BinderRpc, RepeatRootObject) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
- EXPECT_EQ(proc.rootBinder, outBinder);
-}
-
-TEST_P(BinderRpc, NestedTransactions) {
- auto proc = createRpcTestSocketServerProcess({
- // Enable FD support because it uses more stack space and so represents
- // something closer to a worst case scenario.
- .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
- .serverSupportedFileDescriptorTransportModes =
- {RpcSession::FileDescriptorTransportMode::UNIX},
- });
-
- auto nastyNester = sp<MyBinderRpcTest>::make();
- EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
-
- wp<IBinder> weak = nastyNester;
- nastyNester = nullptr;
- EXPECT_EQ(nullptr, weak.promote());
-}
-
-TEST_P(BinderRpc, SameBinderEquality) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> a;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
-
- sp<IBinder> b;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
-
- EXPECT_EQ(a, b);
-}
-
-TEST_P(BinderRpc, SameBinderEqualityWeak) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> a;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
- wp<IBinder> weak = a;
- a = nullptr;
-
- sp<IBinder> b;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
-
- // this is the wrong behavior, since BpBinder
- // doesn't implement onIncStrongAttempted
- // but make sure there is no crash
- EXPECT_EQ(nullptr, weak.promote());
-
- GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
-
- // In order to fix this:
- // - need to have incStrongAttempted reflected across IPC boundary (wait for
- // response to promote - round trip...)
- // - sendOnLastWeakRef, to delete entries out of RpcState table
- EXPECT_EQ(b, weak.promote());
-}
-
-#define expectSessions(expected, iface) \
- do { \
- int session; \
- EXPECT_OK((iface)->getNumOpenSessions(&session)); \
- EXPECT_EQ(expected, session); \
- } while (false)
-
-TEST_P(BinderRpc, SingleSession) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
- std::string out;
- EXPECT_OK(session->getName(&out));
- EXPECT_EQ("aoeu", out);
-
- expectSessions(1, proc.rootIface);
- session = nullptr;
- expectSessions(0, proc.rootIface);
-}
-
-TEST_P(BinderRpc, ManySessions) {
- auto proc = createRpcTestSocketServerProcess({});
-
- std::vector<sp<IBinderRpcSession>> sessions;
-
- for (size_t i = 0; i < 15; i++) {
- expectSessions(i, proc.rootIface);
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
- sessions.push_back(session);
- }
- expectSessions(sessions.size(), proc.rootIface);
- for (size_t i = 0; i < sessions.size(); i++) {
- std::string out;
- EXPECT_OK(sessions.at(i)->getName(&out));
- EXPECT_EQ(std::to_string(i), out);
- }
- expectSessions(sessions.size(), proc.rootIface);
-
- while (!sessions.empty()) {
- sessions.pop_back();
- expectSessions(sessions.size(), proc.rootIface);
- }
- expectSessions(0, proc.rootIface);
-}
-
-size_t epochMillis() {
- using std::chrono::duration_cast;
- using std::chrono::milliseconds;
- using std::chrono::seconds;
- using std::chrono::system_clock;
- return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+ return ret;
}
TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
@@ -857,8 +374,8 @@
for (auto& t : ts) t.join();
}
-void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
+ size_t sleepMs = 500) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
@@ -958,20 +475,6 @@
saturateThreadPool(kNumServerThreads, proc.rootIface);
}
-TEST_P(BinderRpc, OnewayCallDoesNotWait) {
- constexpr size_t kReallyLongTimeMs = 100;
- constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
-
- auto proc = createRpcTestSocketServerProcess({});
-
- size_t epochMsBefore = epochMillis();
-
- EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
-
- size_t epochMsAfter = epochMillis();
- EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
-}
-
TEST_P(BinderRpc, OnewayCallQueueingWithFds) {
if (!supportsFdTransport()) {
GTEST_SKIP() << "Would fail trivially (which is tested elsewhere)";
@@ -1057,7 +560,7 @@
// Build up oneway calls on the second session to make sure it terminates
// and shuts down. The first session should be unaffected (proc destructor
// checks the first session).
- auto iface = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root);
+ auto iface = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
std::vector<std::thread> threads;
for (size_t i = 0; i < kNumClients; i++) {
@@ -1085,66 +588,7 @@
// any pending commands). We need to erase this session from the record
// here, so that the destructor for our session won't check that this
// session is valid, but we still want it to test the other session.
- proc.proc.sessions.erase(proc.proc.sessions.begin() + 1);
-}
-
-TEST_P(BinderRpc, Callbacks) {
- const static std::string kTestString = "good afternoon!";
-
- for (bool callIsOneway : {true, false}) {
- for (bool callbackIsOneway : {true, false}) {
- for (bool delayed : {true, false}) {
- if (clientOrServerSingleThreaded() &&
- (callIsOneway || callbackIsOneway || delayed)) {
- // we have no incoming connections to receive the callback
- continue;
- }
-
- size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
- auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1,
- .numSessions = 1,
- .numIncomingConnections = numIncomingConnections});
- auto cb = sp<MyBinderRpcCallback>::make();
-
- if (callIsOneway) {
- EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed,
- kTestString));
- } else {
- EXPECT_OK(
- proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
- }
-
- // if both transactions are synchronous and the response is sent back on the
- // same thread, everything should have happened in a nested call. Otherwise,
- // the callback will be processed on another thread.
- if (callIsOneway || callbackIsOneway || delayed) {
- using std::literals::chrono_literals::operator""s;
- RpcMutexUniqueLock _l(cb->mMutex);
- cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
- }
-
- EXPECT_EQ(cb->mValues.size(), 1)
- << "callIsOneway: " << callIsOneway
- << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
- if (cb->mValues.empty()) continue;
- EXPECT_EQ(cb->mValues.at(0), kTestString)
- << "callIsOneway: " << callIsOneway
- << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
-
- // since we are severing the connection, we need to go ahead and
- // tell the server to shutdown and exit so that waitpid won't hang
- if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
- EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
- }
-
- // since this session has an incoming connection w/ a threadpool, we
- // need to manually shut it down
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
- proc.expectAlreadyShutdown = true;
- }
- }
- }
+ proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
}
TEST_P(BinderRpc, SingleDeathRecipient) {
@@ -1177,7 +621,7 @@
ASSERT_TRUE(dr->mCv.wait_for(lock, 100ms, [&]() { return dr->dead; }));
// need to wait for the session to shutdown so we don't "Leak session"
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
proc.expectAlreadyShutdown = true;
}
@@ -1205,7 +649,7 @@
// Explicitly calling shutDownAndWait will cause the death recipients
// to be called.
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
std::unique_lock<std::mutex> lock(dr->mMtx);
if (!dr->dead) {
@@ -1213,8 +657,8 @@
}
EXPECT_TRUE(dr->dead) << "Failed to receive the death notification.";
- proc.proc.host.terminate();
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ proc.proc->terminate();
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1259,18 +703,10 @@
}
// need to wait for the session to shutdown so we don't "Leak session"
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
proc.expectAlreadyShutdown = true;
}
-TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
- auto proc = createRpcTestSocketServerProcess({});
- auto cb = sp<MyBinderRpcCallback>::make();
-
- Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
- EXPECT_EQ(WOULD_BLOCK, status.transactionError());
-}
-
TEST_P(BinderRpc, Die) {
for (bool doDeathCleanup : {true, false}) {
auto proc = createRpcTestSocketServerProcess({});
@@ -1286,7 +722,7 @@
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
<< "Do death cleanup: " << doDeathCleanup;
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1316,7 +752,7 @@
// second time! we catch the error :)
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1330,9 +766,9 @@
{RpcSession::FileDescriptorTransportMode::UNIX},
.allowConnectFailure = true,
});
- EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
- proc.proc.host.terminate();
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed";
+ proc.proc->terminate();
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1346,9 +782,9 @@
{RpcSession::FileDescriptorTransportMode::NONE},
.allowConnectFailure = true,
});
- EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
- proc.proc.host.terminate();
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed";
+ proc.proc->terminate();
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1516,16 +952,6 @@
ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
}
-TEST_P(BinderRpc, AidlDelegatorTest) {
- auto proc = createRpcTestSocketServerProcess({});
- auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
- ASSERT_NE(nullptr, myDelegator);
-
- std::string doubled;
- EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
- EXPECT_EQ("cool cool ", doubled);
-}
-
static bool testSupportVsockLoopback() {
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 823bbf6..dc7d264 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -91,6 +91,14 @@
}
}
+static inline size_t epochMillis() {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::seconds;
+ using std::chrono::system_clock;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+}
+
struct BinderRpcOptions {
size_t numThreads = 1;
size_t numSessions = 1;
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
new file mode 100644
index 0000000..721fbfe
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include "binderRpcTestCommon.h"
+
+#define EXPECT_OK(status) \
+ do { \
+ android::binder::Status stat = (status); \
+ EXPECT_TRUE(stat.isOk()) << stat; \
+ } while (false)
+
+namespace android {
+
+// Abstract base class with a virtual destructor that handles the
+// ownership of a process session for BinderRpcTestSession below
+class ProcessSession {
+public:
+ struct SessionInfo {
+ sp<RpcSession> session;
+ sp<IBinder> root;
+ };
+
+ // client session objects associated with other process
+ // each one represents a separate session
+ std::vector<SessionInfo> sessions;
+
+ virtual ~ProcessSession() = 0;
+
+ // If the process exits with a status, run the given callback on that value.
+ virtual void setCustomExitStatusCheck(std::function<void(int wstatus)> f) = 0;
+
+ // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
+ virtual void terminate() = 0;
+};
+
+// Process session where the process hosts IBinderRpcTest, the server used
+// for most testing here
+struct BinderRpcTestProcessSession {
+ std::unique_ptr<ProcessSession> proc;
+
+ // pre-fetched root object (for first session)
+ sp<IBinder> rootBinder;
+
+ // pre-casted root object (for first session)
+ sp<IBinderRpcTest> rootIface;
+
+ // whether session should be invalidated by end of run
+ bool expectAlreadyShutdown = false;
+
+ BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
+ ~BinderRpcTestProcessSession() {
+ if (!expectAlreadyShutdown) {
+ EXPECT_NE(nullptr, rootIface);
+ if (rootIface == nullptr) return;
+
+ std::vector<int32_t> remoteCounts;
+ // calling over any sessions counts across all sessions
+ EXPECT_OK(rootIface->countBinders(&remoteCounts));
+ EXPECT_EQ(remoteCounts.size(), proc->sessions.size());
+ for (auto remoteCount : remoteCounts) {
+ EXPECT_EQ(remoteCount, 1);
+ }
+
+ // even though it is on another thread, shutdown races with
+ // the transaction reply being written
+ if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
+ }
+
+ rootIface = nullptr;
+ rootBinder = nullptr;
+ }
+};
+
+class BinderRpc : public ::testing::TestWithParam<
+ std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
+public:
+ SocketType socketType() const { return std::get<0>(GetParam()); }
+ RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
+ uint32_t clientVersion() const { return std::get<2>(GetParam()); }
+ uint32_t serverVersion() const { return std::get<3>(GetParam()); }
+ bool serverSingleThreaded() const { return std::get<4>(GetParam()); }
+ bool noKernel() const { return std::get<5>(GetParam()); }
+
+ bool clientOrServerSingleThreaded() const {
+ return !kEnableRpcThreads || serverSingleThreaded();
+ }
+
+ // Whether the test params support sending FDs in parcels.
+ bool supportsFdTransport() const {
+ return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
+ (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
+ socketType() == SocketType::UNIX_BOOTSTRAP);
+ }
+
+ void SetUp() override {
+ if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) {
+ GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport";
+ }
+ }
+
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
+ BinderRpcTestProcessSession ret{
+ .proc = createRpcTestSocketServerProcessEtc(options),
+ };
+
+ ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root;
+ ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
+
+ return ret;
+ }
+
+ static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info);
+
+protected:
+ std::unique_ptr<ProcessSession> createRpcTestSocketServerProcessEtc(
+ const BinderRpcOptions& options);
+};
+
+} // namespace android
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
new file mode 100644
index 0000000..1e8d93d
--- /dev/null
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2022 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 <chrono>
+#include <cstdlib>
+#include <type_traits>
+
+#include "binderRpcTestCommon.h"
+#include "binderRpcTestFixture.h"
+
+using namespace std::chrono_literals;
+using namespace std::placeholders;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+
+namespace android {
+
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+ RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+
+TEST(BinderRpcParcel, EntireParcelFormatted) {
+ Parcel p;
+ p.writeInt32(3);
+
+ EXPECT_DEATH_IF_SUPPORTED(p.markForBinder(sp<BBinder>::make()),
+ "format must be set before data is written");
+}
+
+TEST(BinderRpc, CannotUseNextWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
+}
+
+TEST(BinderRpc, CanUseExperimentalWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+}
+
+TEST_P(BinderRpc, Ping) {
+ auto proc = createRpcTestSocketServerProcess({});
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+TEST_P(BinderRpc, GetInterfaceDescriptor) {
+ auto proc = createRpcTestSocketServerProcess({});
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
+}
+
+TEST_P(BinderRpc, MultipleSessions) {
+ if (serverSingleThreaded()) {
+ // Tests with multiple sessions require a multi-threaded service,
+ // but work fine on a single-threaded client
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
+ for (auto session : proc.proc->sessions) {
+ ASSERT_NE(nullptr, session.root);
+ EXPECT_EQ(OK, session.root->pingBinder());
+ }
+}
+
+TEST_P(BinderRpc, SeparateRootObject) {
+ if (serverSingleThreaded()) {
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ SocketType type = std::get<0>(GetParam());
+ if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
+ type == SocketType::UNIX_BOOTSTRAP) {
+ // we can't get port numbers for unix sockets
+ return;
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
+
+ int port1 = 0;
+ EXPECT_OK(proc.rootIface->getClientPort(&port1));
+
+ sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
+ int port2;
+ EXPECT_OK(rootIface2->getClientPort(&port2));
+
+ // we should have a different IBinderRpcTest object created for each
+ // session, because we use setPerSessionRootObject
+ EXPECT_NE(port1, port2);
+}
+
+TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
+ auto proc = createRpcTestSocketServerProcess({});
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
+}
+
+TEST_P(BinderRpc, AppendSeparateFormats) {
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
+
+ Parcel pRaw;
+
+ Parcel p1;
+ p1.markForBinder(proc1.rootBinder);
+ p1.writeInt32(3);
+
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
+ EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
+ Parcel p2;
+ p2.markForBinder(proc2.rootBinder);
+ p2.writeInt32(7);
+
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
+ EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
+}
+
+TEST_P(BinderRpc, UnknownTransaction) {
+ auto proc = createRpcTestSocketServerProcess({});
+ Parcel data;
+ data.markForBinder(proc.rootBinder);
+ Parcel reply;
+ EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
+}
+
+TEST_P(BinderRpc, SendSomethingOneway) {
+ auto proc = createRpcTestSocketServerProcess({});
+ EXPECT_OK(proc.rootIface->sendString("asdf"));
+}
+
+TEST_P(BinderRpc, SendAndGetResultBack) {
+ auto proc = createRpcTestSocketServerProcess({});
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+TEST_P(BinderRpc, SendAndGetResultBackBig) {
+ auto proc = createRpcTestSocketServerProcess({});
+ std::string single = std::string(1024, 'a');
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
+ EXPECT_EQ(single + single, doubled);
+}
+
+TEST_P(BinderRpc, InvalidNullBinderReturn) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
+}
+
+TEST_P(BinderRpc, CallMeBack) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ int32_t pingResult;
+ EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
+ EXPECT_EQ(OK, pingResult);
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST_P(BinderRpc, RepeatBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> inBinder = new MyBinderRpcSession("foo");
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST_P(BinderRpc, RepeatTheirBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+
+ sp<IBinder> inBinder = IInterface::asBinder(session);
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ session = nullptr;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST_P(BinderRpc, RepeatBinderNull) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
+ EXPECT_EQ(nullptr, outBinder);
+}
+
+TEST_P(BinderRpc, HoldBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ IBinder* ptr = nullptr;
+ {
+ sp<IBinder> binder = new BBinder();
+ ptr = binder.get();
+ EXPECT_OK(proc.rootIface->holdBinder(binder));
+ }
+
+ sp<IBinder> held;
+ EXPECT_OK(proc.rootIface->getHeldBinder(&held));
+
+ EXPECT_EQ(held.get(), ptr);
+
+ // stop holding binder, because we test to make sure references are cleaned
+ // up
+ EXPECT_OK(proc.rootIface->holdBinder(nullptr));
+ // and flush ref counts
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+// START TESTS FOR LIMITATIONS OF SOCKET BINDER
+// These are behavioral differences form regular binder, where certain usecases
+// aren't supported.
+
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
+}
+
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+ if (serverSingleThreaded()) {
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(proc.proc->sessions.at(1).root, &outBinder)
+ .transactionError());
+}
+
+TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
+}
+
+TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ // for historical reasons, IServiceManager interface only returns the
+ // exception code
+ EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
+ defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
+}
+
+// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+
+TEST_P(BinderRpc, RepeatRootObject) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
+ EXPECT_EQ(proc.rootBinder, outBinder);
+}
+
+TEST_P(BinderRpc, NestedTransactions) {
+ auto proc = createRpcTestSocketServerProcess({
+ // Enable FD support because it uses more stack space and so represents
+ // something closer to a worst case scenario.
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ auto nastyNester = sp<MyBinderRpcTest>::make();
+ EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
+
+ wp<IBinder> weak = nastyNester;
+ nastyNester = nullptr;
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST_P(BinderRpc, SameBinderEquality) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ EXPECT_EQ(a, b);
+}
+
+TEST_P(BinderRpc, SameBinderEqualityWeak) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ wp<IBinder> weak = a;
+ a = nullptr;
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ // this is the wrong behavior, since BpBinder
+ // doesn't implement onIncStrongAttempted
+ // but make sure there is no crash
+ EXPECT_EQ(nullptr, weak.promote());
+
+ GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
+
+ // In order to fix this:
+ // - need to have incStrongAttempted reflected across IPC boundary (wait for
+ // response to promote - round trip...)
+ // - sendOnLastWeakRef, to delete entries out of RpcState table
+ EXPECT_EQ(b, weak.promote());
+}
+
+#define expectSessions(expected, iface) \
+ do { \
+ int session; \
+ EXPECT_OK((iface)->getNumOpenSessions(&session)); \
+ EXPECT_EQ(expected, session); \
+ } while (false)
+
+TEST_P(BinderRpc, SingleSession) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+ std::string out;
+ EXPECT_OK(session->getName(&out));
+ EXPECT_EQ("aoeu", out);
+
+ expectSessions(1, proc.rootIface);
+ session = nullptr;
+ expectSessions(0, proc.rootIface);
+}
+
+TEST_P(BinderRpc, ManySessions) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ std::vector<sp<IBinderRpcSession>> sessions;
+
+ for (size_t i = 0; i < 15; i++) {
+ expectSessions(i, proc.rootIface);
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
+ sessions.push_back(session);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+ for (size_t i = 0; i < sessions.size(); i++) {
+ std::string out;
+ EXPECT_OK(sessions.at(i)->getName(&out));
+ EXPECT_EQ(std::to_string(i), out);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+
+ while (!sessions.empty()) {
+ sessions.pop_back();
+ expectSessions(sessions.size(), proc.rootIface);
+ }
+ expectSessions(0, proc.rootIface);
+}
+
+TEST_P(BinderRpc, OnewayCallDoesNotWait) {
+ constexpr size_t kReallyLongTimeMs = 100;
+ constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ size_t epochMsBefore = epochMillis();
+
+ EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
+
+ size_t epochMsAfter = epochMillis();
+ EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
+}
+
+TEST_P(BinderRpc, Callbacks) {
+ const static std::string kTestString = "good afternoon!";
+
+ for (bool callIsOneway : {true, false}) {
+ for (bool callbackIsOneway : {true, false}) {
+ for (bool delayed : {true, false}) {
+ if (clientOrServerSingleThreaded() &&
+ (callIsOneway || callbackIsOneway || delayed)) {
+ // we have no incoming connections to receive the callback
+ continue;
+ }
+
+ size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
+ auto proc = createRpcTestSocketServerProcess(
+ {.numThreads = 1,
+ .numSessions = 1,
+ .numIncomingConnections = numIncomingConnections});
+ auto cb = sp<MyBinderRpcCallback>::make();
+
+ if (callIsOneway) {
+ EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed,
+ kTestString));
+ } else {
+ EXPECT_OK(
+ proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
+ }
+
+ // if both transactions are synchronous and the response is sent back on the
+ // same thread, everything should have happened in a nested call. Otherwise,
+ // the callback will be processed on another thread.
+ if (callIsOneway || callbackIsOneway || delayed) {
+ using std::literals::chrono_literals::operator""s;
+ RpcMutexUniqueLock _l(cb->mMutex);
+ cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
+ }
+
+ EXPECT_EQ(cb->mValues.size(), 1)
+ << "callIsOneway: " << callIsOneway
+ << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
+ if (cb->mValues.empty()) continue;
+ EXPECT_EQ(cb->mValues.at(0), kTestString)
+ << "callIsOneway: " << callIsOneway
+ << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
+
+ // since we are severing the connection, we need to go ahead and
+ // tell the server to shutdown and exit so that waitpid won't hang
+ if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
+
+ // since this session has an incoming connection w/ a threadpool, we
+ // need to manually shut it down
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
+ proc.expectAlreadyShutdown = true;
+ }
+ }
+ }
+}
+
+TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
+ auto proc = createRpcTestSocketServerProcess({});
+ auto cb = sp<MyBinderRpcCallback>::make();
+
+ Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
+ EXPECT_EQ(WOULD_BLOCK, status.transactionError());
+}
+
+TEST_P(BinderRpc, AidlDelegatorTest) {
+ auto proc = createRpcTestSocketServerProcess({});
+ auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
+ ASSERT_NE(nullptr, myDelegator);
+
+ std::string doubled;
+ EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+} // namespace android