Merge "binder: make recovery_available."
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index e3c4ede..01c4723 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -265,6 +265,22 @@
chmod 0666 /sys/kernel/tracing/per_cpu/cpu14/trace
chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace
chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu16/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu16/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu17/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu17/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu18/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu18/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu19/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu19/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu20/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu20/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu21/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu21/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu22/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu22/trace
+ chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu23/trace
+ chmod 0666 /sys/kernel/tracing/per_cpu/cpu23/trace
# Only create the tracing instance if persist.mm_events.enabled
# Attempting to remove the tracing instance after it has been created
@@ -337,6 +353,22 @@
chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu14/trace
chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu15/trace
chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu15/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu16/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu16/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu17/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu17/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu18/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu18/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu19/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu19/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu20/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu20/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu21/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu21/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu22/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu22/trace
+ chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace
+ chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace
on property:persist.debug.atrace.boottrace=1
start boottrace
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 83e6787..3722383 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -38,11 +38,6 @@
#include "DumpPool.h"
#include "TaskQueue.h"
-// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
-// std::vector<std::string>
-// TODO: remove once not used
-#define MAX_ARGS_ARRAY_SIZE 1000
-
// TODO: move everything under this namespace
// TODO: and then remove explicitly android::os::dumpstate:: prefixes
namespace android {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 5082eb0..157d259 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -25,6 +25,7 @@
#include <functional>
#include <inttypes.h>
#include <regex>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
@@ -281,36 +282,31 @@
}
status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) {
- auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd));
const binder::Status dump_permission = checkPermission(kDump);
if (!dump_permission.isOk()) {
- out << dump_permission.toString8() << endl;
+ dprintf(fd, "%s\n", dump_permission.toString8().c_str());
return PERMISSION_DENIED;
}
- std::lock_guard<std::recursive_mutex> lock(mLock);
- out << "installd is happy!" << endl;
+ std::lock_guard<std::recursive_mutex> lock(mLock);
{
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
- out << endl << "Storage mounts:" << endl;
+ dprintf(fd, "Storage mounts:\n");
for (const auto& n : mStorageMounts) {
- out << " " << n.first << " = " << n.second << endl;
+ dprintf(fd, " %s = %s\n", n.first.c_str(), n.second.c_str());
}
}
{
std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
- out << endl << "Per-UID cache quotas:" << endl;
+ dprintf(fd, "Per-UID cache quotas:\n");
for (const auto& n : mCacheQuotas) {
- out << " " << n.first << " = " << n.second << endl;
+ dprintf(fd, " %d = %" PRId64 "\n", n.first, n.second);
}
}
- out << "is_dexopt_blocked:" << android::installd::is_dexopt_blocked() << endl;
-
- out << endl;
- out.flush();
+ dprintf(fd, "is_dexopt_blocked:%d\n", android::installd::is_dexopt_blocked());
return NO_ERROR;
}
diff --git a/cmds/rss_hwm_reset/rss_hwm_reset.rc b/cmds/rss_hwm_reset/rss_hwm_reset.rc
index fbbc820..271cbf8 100644
--- a/cmds/rss_hwm_reset/rss_hwm_reset.rc
+++ b/cmds/rss_hwm_reset/rss_hwm_reset.rc
@@ -18,7 +18,7 @@
oneshot
user nobody
group nobody readproc
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
capabilities DAC_OVERRIDE
on property:sys.rss_hwm_reset.on=1
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
index 3e8e3f6..21ac11b 100644
--- a/cmds/service/Android.bp
+++ b/cmds/service/Android.bp
@@ -52,3 +52,21 @@
"-Werror",
],
}
+
+cc_binary_host {
+ name: "aservice",
+
+ srcs: ["service.cpp"],
+
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libbinder",
+ ],
+
+ cflags: [
+ "-DXP_UNIX",
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 18b6b58..fdbe85b 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -50,6 +50,7 @@
{
if (service != nullptr) {
Parcel data, reply;
+ data.markForBinder(service);
status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);
if (err == NO_ERROR) {
return reply.readString16();
@@ -76,6 +77,9 @@
bool wantsUsage = false;
int result = 0;
+ /* Strip path off the program name. */
+ char* prog_name = basename(argv[0]);
+
while (1) {
int ic = getopt(argc, argv, "h?");
if (ic < 0)
@@ -87,7 +91,7 @@
wantsUsage = true;
break;
default:
- aerr << "service: Unknown option -" << ic << endl;
+ aerr << prog_name << ": Unknown option -" << ic << endl;
wantsUsage = true;
result = 10;
break;
@@ -96,10 +100,13 @@
#ifdef VENDORSERVICES
ProcessState::initWithDriver("/dev/vndbinder");
#endif
+#ifndef __ANDROID__
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+#endif
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == nullptr) {
- aerr << "service: Unable to get default service manager!" << endl;
+ aerr << prog_name << ": Unable to get default service manager!" << endl;
return 20;
}
@@ -113,7 +120,7 @@
aout << "Service " << argv[optind] <<
(service == nullptr ? ": not found" : ": found") << endl;
} else {
- aerr << "service: No service specified for check" << endl;
+ aerr << prog_name << ": No service specified for check" << endl;
wantsUsage = true;
result = 10;
}
@@ -138,6 +145,7 @@
int32_t code = atoi(argv[optind++]);
if (service != nullptr && ifName.size() > 0) {
Parcel data, reply;
+ data.markForBinder(service);
// the interface name is first
data.writeInterfaceToken(ifName);
@@ -147,7 +155,7 @@
if (strcmp(argv[optind], "i32") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no integer supplied for 'i32'" << endl;
+ aerr << prog_name << ": no integer supplied for 'i32'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -156,7 +164,7 @@
} else if (strcmp(argv[optind], "i64") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no integer supplied for 'i64'" << endl;
+ aerr << prog_name << ": no integer supplied for 'i64'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -165,7 +173,7 @@
} else if (strcmp(argv[optind], "s16") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no string supplied for 's16'" << endl;
+ aerr << prog_name << ": no string supplied for 's16'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -174,7 +182,7 @@
} else if (strcmp(argv[optind], "f") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no number supplied for 'f'" << endl;
+ aerr << prog_name << ": no number supplied for 'f'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -183,7 +191,7 @@
} else if (strcmp(argv[optind], "d") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no number supplied for 'd'" << endl;
+ aerr << prog_name << ": no number supplied for 'd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -195,7 +203,7 @@
} else if (strcmp(argv[optind], "fd") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no path supplied for 'fd'" << endl;
+ aerr << prog_name << ": no path supplied for 'fd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -203,7 +211,7 @@
const char *path = argv[optind++];
int fd = open(path, O_RDONLY);
if (fd < 0) {
- aerr << "service: could not open '" << path << "'" << endl;
+ aerr << prog_name << ": could not open '" << path << "'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -212,7 +220,7 @@
} else if (strcmp(argv[optind], "afd") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no path supplied for 'afd'" << endl;
+ aerr << prog_name << ": no path supplied for 'afd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -221,7 +229,8 @@
int fd = open(path, O_RDONLY);
struct stat statbuf;
if (fd < 0 || fstat(fd, &statbuf) != 0) {
- aerr << "service: could not open or stat '" << path << "'" << endl;
+ aerr << prog_name << ": could not open or stat"
+ << " '" << path << "'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -229,13 +238,14 @@
int afd = ashmem_create_region("test", statbuf.st_size);
void* ptr = mmap(NULL, statbuf.st_size,
PROT_READ | PROT_WRITE, MAP_SHARED, afd, 0);
- read(fd, ptr, statbuf.st_size);
+ (void)read(fd, ptr, statbuf.st_size);
close(fd);
data.writeFileDescriptor(afd, true /* take ownership */);
} else if (strcmp(argv[optind], "nfd") == 0) {
optind++;
if (optind >= argc) {
- aerr << "service: no file descriptor supplied for 'nfd'" << endl;
+ aerr << prog_name << ": no file descriptor supplied for"
+ << " 'nfd'" << endl;
wantsUsage = true;
result = 10;
break;
@@ -322,7 +332,7 @@
// for now just set the extra field to be null.
data.writeInt32(-1);
} else {
- aerr << "service: unknown option " << argv[optind] << endl;
+ aerr << prog_name << ": unknown option " << argv[optind] << endl;
wantsUsage = true;
result = 10;
break;
@@ -332,44 +342,44 @@
service->transact(code, data, &reply);
aout << "Result: " << reply << endl;
} else {
- aerr << "service: Service " << argv[serviceArg]
+ aerr << prog_name << ": Service " << argv[serviceArg]
<< " does not exist" << endl;
result = 10;
}
} else {
if (optind < argc) {
- aerr << "service: No service specified for call" << endl;
+ aerr << prog_name << ": No service specified for call" << endl;
} else {
- aerr << "service: No code specified for call" << endl;
+ aerr << prog_name << ": No code specified for call" << endl;
}
wantsUsage = true;
result = 10;
}
} else {
- aerr << "service: Unknown command " << argv[optind] << endl;
+ aerr << prog_name << ": Unknown command " << argv[optind] << endl;
wantsUsage = true;
result = 10;
}
}
if (wantsUsage) {
- aout << "Usage: service [-h|-?]\n"
- " service list\n"
- " service check SERVICE\n"
- " service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR | null"
- " | fd f | nfd n | afd f ] ...\n"
+ aout << "Usage: " << prog_name << " [-h|-?]\n"
+ " " << prog_name << " list\n"
+ " " << prog_name << " check SERVICE\n"
+ " " << prog_name << " call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR"
+ " | null | fd f | nfd n | afd f ] ...\n"
"Options:\n"
" i32: Write the 32-bit integer N into the send parcel.\n"
" i64: Write the 64-bit integer N into the send parcel.\n"
- " f: Write the 32-bit single-precision number N into the send parcel.\n"
- " d: Write the 64-bit double-precision number N into the send parcel.\n"
+ " f: Write the 32-bit single-precision number N into the send parcel.\n"
+ " d: Write the 64-bit double-precision number N into the send parcel.\n"
" s16: Write the UTF-16 string STR into the send parcel.\n"
" null: Write a null binder into the send parcel.\n"
- " fd: Write a file descriptor for the file f to the send parcel.\n"
- " nfd: Write file descriptor n to the send parcel.\n"
- " afd: Write an ashmem file descriptor for a region containing the data from"
- " file f to the send parcel.\n";
-// " intent: Write and Intent int the send parcel. ARGS can be\n"
+ " fd: Write a file descriptor for the file f into the send parcel.\n"
+ " nfd: Write the file descriptor n into the send parcel.\n"
+ " afd: Write an ashmem file descriptor for a region containing the data from\n"
+ " file f into the send parcel.\n";
+// " intent: Write an Intent into the send parcel. ARGS can be\n"
// " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n";
return result;
}
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 6d5070f..0dd29e0 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -9,5 +9,5 @@
onrestart class_restart main
onrestart class_restart hal
onrestart class_restart early_hal
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
shutdown critical
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
index 756f6c3..c9305a1 100644
--- a/cmds/servicemanager/vndservicemanager.rc
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -2,7 +2,7 @@
class core
user system
group system readproc
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
onrestart class_restart main
onrestart class_restart hal
onrestart class_restart early_hal
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 3f82939..dc153c9 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -155,10 +155,6 @@
export_aidl_headers: true,
},
- // TODO(b/142684679): for com.android.media which is compiled
- // as vendor and used as system code.
- use_apex_name_macro: true,
-
cflags: [
"-Wall",
"-Wextra",
@@ -337,6 +333,7 @@
"libbase",
"libbinder",
"libbinder_ndk",
+ "liblog",
"libutils",
],
export_include_dirs: ["include_rpc_unstable"],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index d3eef4e..ec9d554 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -547,7 +547,6 @@
AutoMutex _l(e->mLock);
auto rpcServer = RpcServer::make();
LOG_ALWAYS_FATAL_IF(rpcServer == nullptr, "RpcServer::make returns null");
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
auto link = sp<RpcServerLink>::make(rpcServer, keepAliveBinder, weakThis);
if (auto status = keepAliveBinder->linkToDeath(link, nullptr, 0); status != OK) {
ALOGE("%s: keepAliveBinder->linkToDeath returns %s", __PRETTY_FUNCTION__,
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index aff9e0d..81e61da 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -448,21 +448,27 @@
// on-device service manager.
class ServiceManagerHostShim : public ServiceManagerShim {
public:
- using ServiceManagerShim::ServiceManagerShim;
+ ServiceManagerHostShim(const sp<AidlServiceManager>& impl,
+ const RpcDelegateServiceManagerOptions& options)
+ : ServiceManagerShim(impl), mOptions(options) {}
// ServiceManagerShim::getService is based on checkService, so no need to override it.
sp<IBinder> checkService(const String16& name) const override {
- return getDeviceService({String8(name).c_str()});
+ return getDeviceService({String8(name).c_str()}, mOptions);
}
protected:
// Override realGetService for ServiceManagerShim::waitForService.
Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
- *_aidl_return = getDeviceService({"-g", name});
+ *_aidl_return = getDeviceService({"-g", name}, mOptions);
return Status::ok();
}
+
+private:
+ RpcDelegateServiceManagerOptions mOptions;
};
-sp<IServiceManager> createRpcDelegateServiceManager() {
- auto binder = getDeviceService({"manager"});
+sp<IServiceManager> createRpcDelegateServiceManager(
+ const RpcDelegateServiceManagerOptions& options) {
+ auto binder = getDeviceService({"manager"}, options);
if (binder == nullptr) {
ALOGE("getDeviceService(\"manager\") returns null");
return nullptr;
@@ -472,7 +478,7 @@
ALOGE("getDeviceService(\"manager\") returns non service manager");
return nullptr;
}
- return sp<ServiceManagerHostShim>::make(interface);
+ return sp<ServiceManagerHostShim>::make(interface, options);
}
#endif
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index 350994a..1c8bdea 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,5 +1,7 @@
+# Bug component: 32456
arve@google.com
ctate@google.com
hackbod@google.com
maco@google.com
smoreland@google.com
+tkjos@google.com
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 7a30ff9..631a4b6 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -98,9 +98,8 @@
BLOB_ASHMEM_MUTABLE = 2,
};
-static void acquire_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
-{
+static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
+ const void* who) {
switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
@@ -117,13 +116,6 @@
return;
}
case BINDER_TYPE_FD: {
- if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) {
- // If we own an ashmem fd, keep track of how much memory it refers to.
- int size = ashmem_get_size_region(obj.handle);
- if (size > 0) {
- *outAshmemSize += size;
- }
- }
return;
}
}
@@ -131,9 +123,8 @@
ALOGD("Invalid object type 0x%08x", obj.hdr.type);
}
-static void release_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
-{
+static void release_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
+ const void* who) {
switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
@@ -151,16 +142,6 @@
}
case BINDER_TYPE_FD: {
if (obj.cookie != 0) { // owned
- if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) {
- int size = ashmem_get_size_region(obj.handle);
- if (size > 0) {
- // ashmem size might have changed since last time it was accounted for, e.g.
- // in acquire_object(). Value of *outAshmemSize is not critical since we are
- // releasing the object anyway. Check for integer overflow condition.
- *outAshmemSize -= std::min(*outAshmemSize, static_cast<size_t>(size));
- }
- }
-
close(obj.handle);
}
return;
@@ -430,8 +411,9 @@
status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
{
- if (parcel->isForRpc() != isForRpc()) {
- ALOGE("Cannot append Parcel of one format to another.");
+ if (mSession != parcel->mSession) {
+ ALOGE("Cannot append Parcel from one context to another. They may be different formats, "
+ "and objects are specific to a context.");
return BAD_TYPE;
}
@@ -512,7 +494,7 @@
flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData + off);
- acquire_object(proc, *flat, this, &mOpenAshmemSize);
+ acquire_object(proc, *flat, this);
if (flat->hdr.type == BINDER_TYPE_FD) {
// If this is a file descriptor, we need to dup it so the
@@ -566,6 +548,33 @@
return mHasFds;
}
+status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const {
+ if (len > INT32_MAX || offset > INT32_MAX) {
+ // Don't accept size_t values which may have come from an inadvertent conversion from a
+ // negative int.
+ return BAD_VALUE;
+ }
+ size_t limit;
+ if (__builtin_add_overflow(offset, len, &limit) || limit > mDataSize) {
+ return BAD_VALUE;
+ }
+ *result = false;
+ for (size_t i = 0; i < mObjectsSize; i++) {
+ size_t pos = mObjects[i];
+ if (pos < offset) continue;
+ if (pos + sizeof(flat_binder_object) > offset + len) {
+ if (mObjectsSorted) break;
+ else continue;
+ }
+ const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ *result = true;
+ break;
+ }
+ }
+ return NO_ERROR;
+}
+
void Parcel::markSensitive() const
{
mDeallocZero = true;
@@ -1337,7 +1346,7 @@
// Need to write meta-data?
if (nullMetaData || val.binder != 0) {
mObjects[mObjectsSize] = mDataPos;
- acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
+ acquire_object(ProcessState::self(), val, this);
mObjectsSize++;
}
@@ -2227,7 +2236,7 @@
i--;
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(data+objects[i]);
- release_object(proc, *flat, this, &mOpenAshmemSize);
+ release_object(proc, *flat, this);
}
}
@@ -2244,7 +2253,7 @@
i--;
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(data+objects[i]);
- acquire_object(proc, *flat, this, &mOpenAshmemSize);
+ acquire_object(proc, *flat, this);
}
}
@@ -2449,7 +2458,7 @@
// will need to rescan because we may have lopped off the only FDs
mFdsKnown = false;
}
- release_object(proc, *flat, this, &mOpenAshmemSize);
+ release_object(proc, *flat, this);
}
if (objectsSize == 0) {
@@ -2542,7 +2551,6 @@
mAllowFds = true;
mDeallocZero = false;
mOwner = nullptr;
- mOpenAshmemSize = 0;
mWorkSourceRequestHeaderPosition = 0;
mRequestHeaderPresent = false;
@@ -2559,18 +2567,9 @@
}
}
-void Parcel::scanForFds() const
-{
- bool hasFds = false;
- for (size_t i=0; i<mObjectsSize; i++) {
- const flat_binder_object* flat
- = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
- if (flat->hdr.type == BINDER_TYPE_FD) {
- hasFds = true;
- break;
- }
- }
- mHasFds = hasFds;
+void Parcel::scanForFds() const {
+ status_t status = hasFileDescriptorsInRange(0, dataSize(), &mHasFds);
+ ALOGE_IF(status != NO_ERROR, "Error %d calling hasFileDescriptorsInRange()", status);
mFdsKnown = true;
}
@@ -2578,13 +2577,28 @@
{
// This used to return the size of all blobs that were written to ashmem, now we're returning
// the ashmem currently referenced by this Parcel, which should be equivalent.
- // TODO: Remove method once ABI can be changed.
- return mOpenAshmemSize;
+ // TODO(b/202029388): Remove method once ABI can be changed.
+ return getOpenAshmemSize();
}
size_t Parcel::getOpenAshmemSize() const
{
- return mOpenAshmemSize;
+ size_t openAshmemSize = 0;
+ for (size_t i = 0; i < mObjectsSize; i++) {
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
+
+ // cookie is compared against zero for historical reasons
+ // > obj.cookie = takeOwnership ? 1 : 0;
+ if (flat->hdr.type == BINDER_TYPE_FD && flat->cookie != 0 && ashmem_valid(flat->handle)) {
+ int size = ashmem_get_size_region(flat->handle);
+ if (__builtin_add_overflow(openAshmemSize, size, &openAshmemSize)) {
+ ALOGE("Overflow when computing ashmem size.");
+ return SIZE_MAX;
+ }
+ }
+ }
+ return openAshmemSize;
}
// --- Parcel::Blob ---
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 94b2806..4f21cda 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -89,13 +89,21 @@
return init(nullptr, false /*requireDefault*/);
}
+[[clang::no_destroy]] static sp<ProcessState> gProcess;
+[[clang::no_destroy]] static std::mutex gProcessMutex;
+
+static void verifyNotForked(bool forked) {
+ LOG_ALWAYS_FATAL_IF(forked, "libbinder ProcessState can not be used after fork");
+}
+
sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
{
- [[clang::no_destroy]] static sp<ProcessState> gProcess;
- [[clang::no_destroy]] static std::mutex gProcessMutex;
if (driver == nullptr) {
std::lock_guard<std::mutex> l(gProcessMutex);
+ if (gProcess) {
+ verifyNotForked(gProcess->mForked);
+ }
return gProcess;
}
@@ -106,6 +114,14 @@
driver = "/dev/binder";
}
+ // we must install these before instantiating the gProcess object,
+ // otherwise this would race with creating it, and there could be the
+ // possibility of an invalid gProcess object forked by another thread
+ // before these are installed
+ int ret = pthread_atfork(ProcessState::onFork, ProcessState::parentPostFork,
+ ProcessState::childPostFork);
+ LOG_ALWAYS_FATAL_IF(ret != 0, "pthread_atfork error %s", strerror(ret));
+
std::lock_guard<std::mutex> l(gProcessMutex);
gProcess = sp<ProcessState>::make(driver);
});
@@ -119,6 +135,7 @@
gProcess->getDriverName().c_str(), driver);
}
+ verifyNotForked(gProcess->mForked);
return gProcess;
}
@@ -137,6 +154,24 @@
return context;
}
+void ProcessState::onFork() {
+ // make sure another thread isn't currently retrieving ProcessState
+ gProcessMutex.lock();
+}
+
+void ProcessState::parentPostFork() {
+ gProcessMutex.unlock();
+}
+
+void ProcessState::childPostFork() {
+ // another thread might call fork before gProcess is instantiated, but after
+ // the thread handler is installed
+ if (gProcess) {
+ gProcess->mForked = true;
+ }
+ gProcessMutex.unlock();
+}
+
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
@@ -426,6 +461,7 @@
mWaitingForThreads(0),
mMaxThreads(DEFAULT_MAX_BINDER_THREADS),
mStarvationStartTimeMs(0),
+ mForked(false),
mThreadPoolStarted(false),
mThreadPoolSeq(1),
mCallRestriction(CallRestriction::NONE) {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 967b8e3..93ed50e 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -58,10 +58,6 @@
return sp<RpcServer>::make(std::move(ctx));
}
-void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
- mAgreedExperimental = true;
-}
-
status_t RpcServer::setupUnixDomainServer(const char* path) {
return setupSocketServer(UnixSocketAddress(path));
}
@@ -127,14 +123,23 @@
void RpcServer::setRootObject(const sp<IBinder>& binder) {
std::lock_guard<std::mutex> _l(mLock);
+ mRootObjectFactory = nullptr;
mRootObjectWeak = mRootObject = binder;
}
void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
std::lock_guard<std::mutex> _l(mLock);
mRootObject.clear();
+ mRootObjectFactory = nullptr;
mRootObjectWeak = binder;
}
+void RpcServer::setPerSessionRootObject(
+ std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& makeObject) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mRootObject.clear();
+ mRootObjectWeak.clear();
+ mRootObjectFactory = std::move(makeObject);
+}
sp<IBinder> RpcServer::getRootObject() {
std::lock_guard<std::mutex> _l(mLock);
@@ -154,14 +159,12 @@
}
void RpcServer::start() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!");
mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
}
void RpcServer::join() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
{
std::lock_guard<std::mutex> _l(mLock);
@@ -174,8 +177,14 @@
status_t status;
while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) {
- unique_fd clientFd(TEMP_FAILURE_RETRY(
- accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC | SOCK_NONBLOCK)));
+ sockaddr_storage addr;
+ socklen_t addrLen = sizeof(addr);
+
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr),
+ &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK)));
+
+ LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address");
if (clientFd < 0) {
ALOGE("Could not accept4 socket: %s", strerror(errno));
@@ -187,7 +196,7 @@
std::lock_guard<std::mutex> _l(mLock);
std::thread thread =
std::thread(&RpcServer::establishConnection, sp<RpcServer>::fromExisting(this),
- std::move(clientFd));
+ std::move(clientFd), addr, addrLen);
mConnectingThreads[thread.get_id()] = std::move(thread);
}
}
@@ -257,10 +266,8 @@
return mConnectingThreads.size();
}
-void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) {
- // TODO(b/183988761): cannot trust this simple ID
- LOG_ALWAYS_FATAL_IF(!server->mAgreedExperimental, "no!");
-
+void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
+ const sockaddr_storage addr, socklen_t addrLen) {
// mShutdownTrigger can only be cleared once connection threads have joined.
// It must be set before this thread is started
LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr);
@@ -383,11 +390,23 @@
session = RpcSession::make();
session->setMaxIncomingThreads(server->mMaxThreads);
if (!session->setProtocolVersion(protocolVersion)) return;
+
+ // if null, falls back to server root
+ sp<IBinder> sessionSpecificRoot;
+ if (server->mRootObjectFactory != nullptr) {
+ sessionSpecificRoot =
+ server->mRootObjectFactory(reinterpret_cast<const sockaddr*>(&addr),
+ addrLen);
+ if (sessionSpecificRoot == nullptr) {
+ ALOGE("Warning: server returned null from root object factory");
+ }
+ }
+
if (!session->setForServer(server,
sp<RpcServer::EventListener>::fromExisting(
static_cast<RpcServer::EventListener*>(
server.get())),
- sessionId)) {
+ sessionId, sessionSpecificRoot)) {
ALOGE("Failed to attach server to session");
return;
}
@@ -478,19 +497,16 @@
}
bool RpcServer::hasServer() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
return mServer.ok();
}
unique_fd RpcServer::releaseServer() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
return std::move(mServer);
}
status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
if (mServer.ok()) {
ALOGE("Each RpcServer can only have one server.");
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 486b67b..53c9b78 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -90,6 +90,20 @@
return mMaxIncomingThreads;
}
+void RpcSession::setMaxOutgoingThreads(size_t threads) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
+ "Must set max outgoing threads before setting up connections, but has %zu "
+ "client(s) and %zu server(s)",
+ mConnections.mOutgoing.size(), mConnections.mIncoming.size());
+ mMaxOutgoingThreads = threads;
+}
+
+size_t RpcSession::getMaxOutgoingThreads() {
+ std::lock_guard<std::mutex> _l(mMutex);
+ return mMaxOutgoingThreads;
+}
+
bool RpcSession::setProtocolVersion(uint32_t version) {
if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
@@ -473,6 +487,12 @@
return status;
}
+ size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
+ ALOGI_IF(outgoingThreads != numThreadsAvailable,
+ "Server hints client to start %zu outgoing threads, but client will only start %zu "
+ "because it is preconfigured to start at most %zu outgoing threads.",
+ numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads);
+
// TODO(b/189955605): we should add additional sessions dynamically
// instead of all at once - the other side should be responsible for setting
// up additional connections. We need to create at least one (unless 0 are
@@ -480,7 +500,10 @@
// any requests at all.
// we've already setup one client
- for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+ LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu "
+ "incoming threads",
+ outgoingThreads, numThreadsAvailable, mMaxIncomingThreads);
+ for (size_t i = 0; i + 1 < outgoingThreads; i++) {
if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
}
@@ -665,7 +688,8 @@
status_t status = OK;
if (init) {
- mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this));
+ status =
+ mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this));
}
{
@@ -677,7 +701,8 @@
}
bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListener>& eventListener,
- const std::vector<uint8_t>& sessionId) {
+ const std::vector<uint8_t>& sessionId,
+ const sp<IBinder>& sessionSpecificRoot) {
LOG_ALWAYS_FATAL_IF(mForServer != nullptr);
LOG_ALWAYS_FATAL_IF(server == nullptr);
LOG_ALWAYS_FATAL_IF(mEventListener != nullptr);
@@ -690,6 +715,7 @@
mId = sessionId;
mForServer = server;
mEventListener = eventListener;
+ mSessionSpecificRootObject = sessionSpecificRoot;
return true;
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 9ba64f3..09b3d68 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -870,7 +870,9 @@
if (server) {
switch (transaction->code) {
case RPC_SPECIAL_TRANSACT_GET_ROOT: {
- replyStatus = reply.writeStrongBinder(server->getRootObject());
+ sp<IBinder> root = session->mSessionSpecificRootObject
+ ?: server->getRootObject();
+ replyStatus = reply.writeStrongBinder(root);
break;
}
default: {
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 50de22b..dba0a43 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -60,20 +60,21 @@
RpcState();
~RpcState();
- status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, uint32_t* version);
- status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session);
- status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session);
+ [[nodiscard]] status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, uint32_t* version);
+ [[nodiscard]] status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session);
+ [[nodiscard]] status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session);
// TODO(b/182940634): combine some special transactions into one "getServerInfo" call?
sp<IBinder> getRootObject(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session);
- status_t getMaxThreads(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, size_t* maxThreadsOut);
- status_t getSessionId(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, std::vector<uint8_t>* sessionIdOut);
+ [[nodiscard]] status_t getMaxThreads(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, size_t* maxThreadsOut);
+ [[nodiscard]] status_t getSessionId(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session,
+ std::vector<uint8_t>* sessionIdOut);
[[nodiscard]] status_t transact(const sp<RpcSession::RpcConnection>& connection,
const sp<IBinder>& address, uint32_t code, const Parcel& data,
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 27cc563..194254a 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -124,7 +124,8 @@
} // namespace
-sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs) {
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs,
+ const RpcDelegateServiceManagerOptions& options) {
std::vector<std::string> prefix{"adb", "shell", "servicedispatcher"};
serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end());
@@ -158,6 +159,10 @@
LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
auto rpcSession = RpcSession::make();
+ if (options.maxOutgoingThreads.has_value()) {
+ rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads);
+ }
+
if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
status != OK) {
ALOGE("Unable to set up inet client on host port %u: %s", *forwardResult->hostPort(),
diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h
index e59724c..c5310da 100644
--- a/libs/binder/ServiceManagerHost.h
+++ b/libs/binder/ServiceManagerHost.h
@@ -21,11 +21,14 @@
namespace android {
+struct RpcDelegateServiceManagerOptions;
+
// Get a service on device by running servicedispatcher with the given args, e.g.
// getDeviceService({"foo"});
// Return nullptr on any error.
// When the returned binder object is destroyed, remove adb forwarding and kills
// the long-running servicedispatcher process.
-sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs);
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs,
+ const RpcDelegateServiceManagerOptions& options);
} // namespace android
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index a48075d..240e3c2 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -188,7 +188,16 @@
// // ...
// }
// Resources are cleaned up when the object is destroyed.
-sp<IServiceManager> createRpcDelegateServiceManager();
+//
+// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated.
+// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked
+// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads.
+// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|.
+struct RpcDelegateServiceManagerOptions {
+ std::optional<size_t> maxOutgoingThreads;
+};
+sp<IServiceManager> createRpcDelegateServiceManager(
+ const RpcDelegateServiceManagerOptions& options);
#endif
} // namespace android
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index fd8ac62..d90e803 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -87,6 +87,7 @@
void restoreAllowFds(bool lastValue);
bool hasFileDescriptors() const;
+ status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const;
// Zeros data when reallocating. Other mitigations may be added
// in the future.
@@ -1141,6 +1142,7 @@
release_func mOwner;
sp<RpcSession> mSession;
+ size_t mReserved;
class Blob {
public:
@@ -1225,13 +1227,19 @@
inline void* data() { return mData; }
};
-private:
- size_t mOpenAshmemSize;
-
-public:
- // TODO: Remove once ABI can be changed.
- size_t getBlobAshmemSize() const;
+ /**
+ * Returns the total amount of ashmem memory owned by this object.
+ *
+ * Note: for historical reasons, this does not include ashmem memory which
+ * is referenced by this Parcel, but which this parcel doesn't own (e.g.
+ * writeFileDescriptor is called without 'takeOwnership' true).
+ */
size_t getOpenAshmemSize() const;
+
+private:
+ // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference
+ // this
+ size_t getBlobAshmemSize() const;
};
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 72c2ab7..cf8d8e4 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -94,6 +94,10 @@
private:
static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
+ static void onFork();
+ static void parentPostFork();
+ static void childPostFork();
+
friend class IPCThreadState;
friend class sp<ProcessState>;
@@ -132,6 +136,7 @@
Vector<handle_entry> mHandleToObject;
+ bool mForked;
bool mThreadPoolStarted;
volatile int32_t mThreadPoolSeq;
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index fb2cf23..aaa812b 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -25,10 +25,6 @@
#include <mutex>
#include <thread>
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
namespace android {
class FdTrigger;
@@ -99,8 +95,6 @@
*/
[[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
- void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
/**
* This must be called before adding a client session.
*
@@ -130,6 +124,10 @@
* Holds a weak reference to the root object.
*/
void setRootObjectWeak(const wp<IBinder>& binder);
+ /**
+ * Allows a root object to be created for each session
+ */
+ void setPerSessionRootObject(std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& object);
sp<IBinder> getRootObject();
/**
@@ -179,11 +177,11 @@
void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
- static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd);
- status_t setupSocketServer(const RpcSocketAddress& address);
+ static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
+ const sockaddr_storage addr, socklen_t addrLen);
+ [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
const std::unique_ptr<RpcTransportCtx> mCtx;
- bool mAgreedExperimental = false;
size_t mMaxThreads = 1;
std::optional<uint32_t> mProtocolVersion;
base::unique_fd mServer; // socket we are accepting sessions on
@@ -194,6 +192,7 @@
std::map<std::thread::id, std::thread> mConnectingThreads;
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
+ std::function<sp<IBinder>(const sockaddr*, socklen_t)> mRootObjectFactory;
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
std::condition_variable mShutdownCv;
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index e64572b..1bc8464 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -26,10 +26,6 @@
#include <thread>
#include <vector>
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
namespace android {
class Parcel;
@@ -50,6 +46,8 @@
*/
class RpcSession final : public virtual RefBase {
public:
+ static constexpr size_t kDefaultMaxOutgoingThreads = 10;
+
// Create an RpcSession with default configuration (raw sockets).
static sp<RpcSession> make();
@@ -72,6 +70,18 @@
size_t getMaxIncomingThreads();
/**
+ * Set the maximum number of outgoing threads allowed to be made.
+ * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this
+ * connection as a client.
+ *
+ * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession
+ * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where
+ * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|.
+ */
+ void setMaxOutgoingThreads(size_t threads);
+ size_t getMaxOutgoingThreads();
+
+ /**
* By default, the minimum of the supported versions of the client and the
* server will be used. Usually, this API should only be used for debugging.
*/
@@ -126,7 +136,7 @@
* Query the other side of the session for the maximum number of threads
* it supports (maximum number of concurrent non-nested synchronous transactions)
*/
- status_t getRemoteMaxThreads(size_t* maxThreads);
+ [[nodiscard]] status_t getRemoteMaxThreads(size_t* maxThreads);
/**
* See RpcTransportCtx::getCertificate
@@ -206,7 +216,7 @@
bool allowNested = false;
};
- status_t readId();
+ [[nodiscard]] status_t readId();
// A thread joining a server must always call these functions in order, and
// cleanup is only programmed once into join. These are in separate
@@ -242,12 +252,13 @@
bool init);
[[nodiscard]] bool setForServer(const wp<RpcServer>& server,
const wp<RpcSession::EventListener>& eventListener,
- const std::vector<uint8_t>& sessionId);
+ const std::vector<uint8_t>& sessionId,
+ const sp<IBinder>& sessionSpecificRoot);
sp<RpcConnection> assignIncomingConnectionToThisThread(
std::unique_ptr<RpcTransport> rpcTransport);
[[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
- status_t initShutdownTrigger();
+ [[nodiscard]] status_t initShutdownTrigger();
enum class ConnectionUse {
CLIENT,
@@ -258,8 +269,8 @@
// Object representing exclusive access to a connection.
class ExclusiveConnection {
public:
- static status_t find(const sp<RpcSession>& session, ConnectionUse use,
- ExclusiveConnection* connection);
+ [[nodiscard]] static status_t find(const sp<RpcSession>& session, ConnectionUse use,
+ ExclusiveConnection* connection);
~ExclusiveConnection();
const sp<RpcConnection>& get() { return mConnection; }
@@ -299,6 +310,10 @@
sp<WaitForShutdownListener> mShutdownListener; // used for client sessions
wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client
+ // session-specific root object (if a different root is used for each
+ // session)
+ sp<IBinder> mSessionSpecificRootObject;
+
std::vector<uint8_t> mId;
std::unique_ptr<FdTrigger> mShutdownTrigger;
@@ -308,6 +323,7 @@
std::mutex mMutex; // for all below
size_t mMaxIncomingThreads = 0;
+ size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
std::optional<uint32_t> mProtocolVersion;
std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 08f5eed..34f1cbf 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -16,6 +16,8 @@
#pragma once
+#include <sys/socket.h>
+
extern "C" {
struct AIBinder;
@@ -30,6 +32,12 @@
bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
void* param);
+// Starts an RPC server on a given port and a given root IBinder object.
+// This function sets up the server, calls readyCallback with a given param, and
+// then joins before returning.
+bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port);
+
AIBinder* RpcClient(unsigned int cid, unsigned int port);
// Connect to an RPC server with preconnected file descriptors.
diff --git a/libs/binder/include_tls/binder/RpcAuth.h b/libs/binder/include_tls/binder/RpcAuth.h
index 4c2f296..ab64828 100644
--- a/libs/binder/include_tls/binder/RpcAuth.h
+++ b/libs/binder/include_tls/binder/RpcAuth.h
@@ -40,7 +40,7 @@
// - SSL_CTX_use_certificate
// - SSL_CTX_set*_chain
// - SSL_CTX_add0_chain_cert
- virtual status_t configure(SSL_CTX* ctx) = 0;
+ [[nodiscard]] virtual status_t configure(SSL_CTX* ctx) = 0;
};
} // namespace android
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index cad55fb..bf2b25b 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -19,6 +19,7 @@
#include <android/binder_libbinder.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+#include <linux/vm_sockets.h>
using android::OK;
using android::RpcServer;
@@ -29,10 +30,31 @@
extern "C" {
+bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port) {
+ auto server = RpcServer::make();
+ if (status_t status = server->setupVsockServer(port); status != OK) {
+ LOG(ERROR) << "Failed to set up vsock server with port " << port
+ << " error: " << statusToString(status).c_str();
+ return false;
+ }
+ server->setPerSessionRootObject([=](const sockaddr* addr, socklen_t addrlen) {
+ LOG_ALWAYS_FATAL_IF(addr->sa_family != AF_VSOCK, "address is not a vsock");
+ LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
+ const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
+ return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
+ });
+
+ server->join();
+
+ // Shutdown any open sessions since server failed.
+ (void)server->shutdown();
+ return true;
+}
+
bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
void* param) {
auto server = RpcServer::make();
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
if (status_t status = server->setupVsockServer(port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
<< " error: " << statusToString(status).c_str();
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 49c7b7c..81aa551 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -780,3 +780,7 @@
AIBinder_incStrong(ndkBinder.get());
return ndkBinder.get();
}
+
+void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) {
+ binder->asABBinder()->setMinSchedulerPolicy(policy, priority);
+}
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 563d011..2b18a0a 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -543,6 +543,28 @@
}
/**
+ * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
+ */
+template <typename P>
+binder_status_t AParcel_writeNullableStdVectorParcelableElement(AParcel* parcel,
+ const void* vectorData,
+ size_t index) {
+ const std::optional<std::vector<P>>* vector =
+ static_cast<const std::optional<std::vector<P>>*>(vectorData);
+ return AParcel_writeNullableParcelable(parcel, (*vector)->at(index));
+}
+
+/**
+ * Reads a parcelable object of type P inside a std::vector<P> at index 'index' from 'parcel'.
+ */
+template <typename P>
+binder_status_t AParcel_readNullableStdVectorParcelableElement(const AParcel* parcel,
+ void* vectorData, size_t index) {
+ std::optional<std::vector<P>>* vector = static_cast<std::optional<std::vector<P>>*>(vectorData);
+ return AParcel_readNullableParcelable(parcel, &(*vector)->at(index));
+}
+
+/**
* Writes a ScopedFileDescriptor object inside a std::vector<ScopedFileDescriptor> at index 'index'
* to 'parcel'.
*/
@@ -551,11 +573,7 @@
AParcel* parcel, const void* vectorData, size_t index) {
const std::vector<ScopedFileDescriptor>* vector =
static_cast<const std::vector<ScopedFileDescriptor>*>(vectorData);
- int writeFd = vector->at(index).get();
- if (writeFd < 0) {
- return STATUS_UNEXPECTED_NULL;
- }
- return AParcel_writeParcelFileDescriptor(parcel, writeFd);
+ return AParcel_writeRequiredParcelFileDescriptor(parcel, vector->at(index));
}
/**
@@ -567,15 +585,79 @@
const AParcel* parcel, void* vectorData, size_t index) {
std::vector<ScopedFileDescriptor>* vector =
static_cast<std::vector<ScopedFileDescriptor>*>(vectorData);
- int readFd;
- binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd);
- if (status == STATUS_OK) {
- if (readFd < 0) {
- return STATUS_UNEXPECTED_NULL;
- }
- vector->at(index).set(readFd);
- }
- return status;
+ return AParcel_readRequiredParcelFileDescriptor(parcel, &vector->at(index));
+}
+
+/**
+ * Writes a ScopedFileDescriptor object inside a std::optional<std::vector<ScopedFileDescriptor>> at
+ * index 'index' to 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_writeNullableStdVectorParcelableElement<ScopedFileDescriptor>(
+ AParcel* parcel, const void* vectorData, size_t index) {
+ const std::optional<std::vector<ScopedFileDescriptor>>* vector =
+ static_cast<const std::optional<std::vector<ScopedFileDescriptor>>*>(vectorData);
+ return AParcel_writeNullableParcelFileDescriptor(parcel, (*vector)->at(index));
+}
+
+/**
+ * Reads a ScopedFileDescriptor object inside a std::optional<std::vector<ScopedFileDescriptor>> at
+ * index 'index' from 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_readNullableStdVectorParcelableElement<ScopedFileDescriptor>(
+ const AParcel* parcel, void* vectorData, size_t index) {
+ std::optional<std::vector<ScopedFileDescriptor>>* vector =
+ static_cast<std::optional<std::vector<ScopedFileDescriptor>>*>(vectorData);
+ return AParcel_readNullableParcelFileDescriptor(parcel, &(*vector)->at(index));
+}
+
+/**
+ * Writes an SpAIBinder object inside a std::vector<SpAIBinder> at index 'index'
+ * to 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_writeStdVectorParcelableElement<SpAIBinder>(AParcel* parcel,
+ const void* vectorData,
+ size_t index) {
+ const std::vector<SpAIBinder>* vector = static_cast<const std::vector<SpAIBinder>*>(vectorData);
+ return AParcel_writeRequiredStrongBinder(parcel, vector->at(index));
+}
+
+/**
+ * Reads an SpAIBinder object inside a std::vector<SpAIBinder> at index 'index'
+ * from 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_readStdVectorParcelableElement<SpAIBinder>(const AParcel* parcel,
+ void* vectorData,
+ size_t index) {
+ std::vector<SpAIBinder>* vector = static_cast<std::vector<SpAIBinder>*>(vectorData);
+ return AParcel_readRequiredStrongBinder(parcel, &vector->at(index));
+}
+
+/**
+ * Writes an SpAIBinder object inside a std::optional<std::vector<SpAIBinder>> at index 'index'
+ * to 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_writeNullableStdVectorParcelableElement<SpAIBinder>(
+ AParcel* parcel, const void* vectorData, size_t index) {
+ const std::optional<std::vector<SpAIBinder>>* vector =
+ static_cast<const std::optional<std::vector<SpAIBinder>>*>(vectorData);
+ return AParcel_writeNullableStrongBinder(parcel, (*vector)->at(index));
+}
+
+/**
+ * Reads an SpAIBinder object inside a std::optional<std::vector<SpAIBinder>> at index 'index'
+ * from 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_readNullableStdVectorParcelableElement<SpAIBinder>(
+ const AParcel* parcel, void* vectorData, size_t index) {
+ std::optional<std::vector<SpAIBinder>>* vector =
+ static_cast<std::optional<std::vector<SpAIBinder>>*>(vectorData);
+ return AParcel_readNullableStrongBinder(parcel, &(*vector)->at(index));
}
/**
@@ -598,6 +680,30 @@
AParcel_readStdVectorParcelableElement<P>);
}
+/**
+ * Convenience API for writing a std::optional<std::vector<P>>
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeVector(AParcel* parcel,
+ const std::optional<std::vector<P>>& vec) {
+ if (!vec) return AParcel_writeInt32(parcel, -1);
+ const void* vectorData = static_cast<const void*>(&vec);
+ return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
+ AParcel_writeNullableStdVectorParcelableElement<P>);
+}
+
+/**
+ * Convenience API for reading a std::optional<std::vector<P>>
+ */
+template <typename P>
+static inline binder_status_t AParcel_readVector(const AParcel* parcel,
+ std::optional<std::vector<P>>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readParcelableArray(parcel, vectorData,
+ AParcel_nullableStdVectorExternalAllocator<P>,
+ AParcel_readNullableStdVectorParcelableElement<P>);
+}
+
// @START
/**
* Writes a vector of int32_t to the next location in a non-null parcel.
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index e315c79..b0217c4 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -55,4 +55,17 @@
*/
__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
+/**
+ * Sets a minimum scheduler policy for all transactions coming into this
+ * AIBinder.
+ *
+ * This must be called before the object is sent to another process.
+ * Aborts on invalid values. Not thread safe.
+ *
+ * \param binder local server binder to set the policy for
+ * \param policy scheduler policy as defined in linux UAPI
+ * \param priority priority. [-20..19] for SCHED_NORMAL, [1..99] for RT
+ */
+void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) __INTRODUCED_IN(33);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 8605686..64170af 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_setMinSchedulerPolicy; # llndk
AParcel_marshal;
AParcel_unmarshal;
};
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index d9d7caf..ecb044e 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -17,6 +17,7 @@
rustlibs: [
"liblibc",
"libbinder_ndk_sys",
+ "libdowncast_rs",
],
host_supported: true,
target: {
@@ -133,6 +134,7 @@
rustlibs: [
"liblibc",
"libbinder_ndk_sys",
+ "libdowncast_rs",
],
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 41ceee5..cc5dd06 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -17,12 +17,13 @@
//! Trait definitions for binder objects
use crate::error::{status_t, Result, StatusCode};
-use crate::parcel::Parcel;
+use crate::parcel::{OwnedParcel, Parcel};
use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
use crate::sys;
use std::borrow::Borrow;
use std::cmp::Ordering;
+use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fmt;
use std::fs::File;
@@ -70,6 +71,7 @@
/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
/// makes no stability guarantees ([`Local`]). [`Local`] is
/// currently the default stability.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Stability {
/// Default stability, visible to other modules in the same compilation
/// context (e.g. modules on system.img)
@@ -85,6 +87,28 @@
}
}
+impl From<Stability> for i32 {
+ fn from(stability: Stability) -> i32 {
+ use Stability::*;
+ match stability {
+ Local => 0,
+ Vintf => 1,
+ }
+ }
+}
+
+impl TryFrom<i32> for Stability {
+ type Error = StatusCode;
+ fn try_from(stability: i32) -> Result<Stability> {
+ use Stability::*;
+ match stability {
+ 0 => Ok(Local),
+ 1 => Ok(Vintf),
+ _ => Err(StatusCode::BAD_VALUE)
+ }
+ }
+}
+
/// A local service that can be remotable via Binder.
///
/// An object that implement this interface made be made into a Binder service
@@ -153,25 +177,25 @@
fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
/// Create a Parcel that can be used with `submit_transact`.
- fn prepare_transact(&self) -> Result<Parcel>;
+ fn prepare_transact(&self) -> Result<OwnedParcel>;
/// Perform a generic operation with the object.
///
- /// The provided [`Parcel`] must have been created by a call to
+ /// The provided [`OwnedParcel`] must have been created by a call to
/// `prepare_transact` on the same binder.
///
/// # Arguments
///
/// * `code` - Transaction code for the operation.
- /// * `data` - [`Parcel`] with input data.
+ /// * `data` - [`OwnedParcel`] with input data.
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)).
fn submit_transact(
&self,
code: TransactionCode,
- data: Parcel,
+ data: OwnedParcel,
flags: TransactionFlags,
- ) -> Result<Parcel>;
+ ) -> Result<OwnedParcel>;
/// Perform a generic operation with the object. This is a convenience
/// method that internally calls `prepare_transact` followed by
@@ -189,8 +213,8 @@
input_callback: F,
) -> Result<Parcel> {
let mut parcel = self.prepare_transact()?;
- input_callback(&mut parcel)?;
- self.submit_transact(code, parcel, flags)
+ input_callback(&mut parcel.borrowed())?;
+ self.submit_transact(code, parcel, flags).map(OwnedParcel::into_parcel)
}
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 7e8e3a5..81b620e 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -113,7 +113,7 @@
};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder};
-pub use parcel::Parcel;
+pub use parcel::{OwnedParcel, Parcel};
pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
pub use state::{ProcessState, ThreadState};
@@ -127,7 +127,7 @@
/// The public API usable outside AIDL-generated interface crates.
pub mod public_api {
- pub use super::parcel::ParcelFileDescriptor;
+ pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder};
pub use super::{
add_service, force_lazy_services_persist, get_interface, register_lazy_service,
wait_for_interface,
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index dad89ec..9dba950 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -23,17 +23,21 @@
use std::cell::RefCell;
use std::convert::TryInto;
+use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ptr;
use std::fmt;
mod file_descriptor;
mod parcelable;
+mod parcelable_holder;
pub use self::file_descriptor::ParcelFileDescriptor;
pub use self::parcelable::{
Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption,
+ Parcelable, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
};
+pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata};
/// Container for a message (data and object references) that can be sent
/// through Binder.
@@ -49,6 +53,106 @@
Borrowed(*mut sys::AParcel),
}
+/// A variant of Parcel that is known to be owned.
+pub struct OwnedParcel {
+ ptr: *mut sys::AParcel,
+}
+
+/// # Safety
+///
+/// This type guarantees that it owns the AParcel and that all access to
+/// the AParcel happens through the OwnedParcel, so it is ok to send across
+/// threads.
+unsafe impl Send for OwnedParcel {}
+
+/// A variant of Parcel that is known to be borrowed.
+pub struct BorrowedParcel<'a> {
+ inner: Parcel,
+ _lifetime: PhantomData<&'a mut Parcel>,
+}
+
+impl OwnedParcel {
+ /// Create a new empty `OwnedParcel`.
+ pub fn new() -> OwnedParcel {
+ let ptr = unsafe {
+ // Safety: If `AParcel_create` succeeds, it always returns
+ // a valid pointer. If it fails, the process will crash.
+ sys::AParcel_create()
+ };
+ assert!(!ptr.is_null());
+ Self { ptr }
+ }
+
+ /// Create an owned reference to a parcel object from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe if the raw pointer parameter is either null
+ /// (resulting in `None`), or a valid pointer to an `AParcel` object. The
+ /// parcel object must be owned by the caller prior to this call, as this
+ /// constructor takes ownership of the parcel and will destroy it on drop.
+ ///
+ /// Additionally, the caller must guarantee that it is valid to take
+ /// ownership of the AParcel object. All future access to the AParcel
+ /// must happen through this `OwnedParcel`.
+ ///
+ /// Because `OwnedParcel` implements `Send`, the pointer must never point
+ /// to any thread-local data, e.g., a variable on the stack, either directly
+ /// or indirectly.
+ pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<OwnedParcel> {
+ ptr.as_mut().map(|ptr| Self { ptr })
+ }
+
+ /// Consume the parcel, transferring ownership to the caller.
+ pub(crate) fn into_raw(self) -> *mut sys::AParcel {
+ let ptr = self.ptr;
+ let _ = ManuallyDrop::new(self);
+ ptr
+ }
+
+ /// Convert this `OwnedParcel` into an owned `Parcel`.
+ pub fn into_parcel(self) -> Parcel {
+ Parcel::Owned(self.into_raw())
+ }
+
+ /// Get a borrowed view into the contents of this `Parcel`.
+ pub fn borrowed(&mut self) -> BorrowedParcel<'_> {
+ BorrowedParcel {
+ inner: Parcel::Borrowed(self.ptr),
+ _lifetime: PhantomData,
+ }
+ }
+}
+
+impl Default for OwnedParcel {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Clone for OwnedParcel {
+ fn clone(&self) -> Self {
+ let mut new_parcel = Self::new();
+ new_parcel
+ .borrowed()
+ .append_all_from(&Parcel::Borrowed(self.ptr))
+ .expect("Failed to append from Parcel");
+ new_parcel
+ }
+}
+
+impl<'a> std::ops::Deref for BorrowedParcel<'a> {
+ type Target = Parcel;
+ fn deref(&self) -> &Parcel {
+ &self.inner
+ }
+}
+impl<'a> std::ops::DerefMut for BorrowedParcel<'a> {
+ fn deref_mut(&mut self) -> &mut Parcel {
+ &mut self.inner
+ }
+}
+
/// # Safety
///
/// The `Parcel` constructors guarantee that a `Parcel` object will always
@@ -68,6 +172,21 @@
}
impl Parcel {
+ /// Create a new empty `Parcel`.
+ ///
+ /// Creates a new owned empty parcel that can be written to
+ /// using the serialization methods and appended to and
+ /// from using `append_from` and `append_from_all`.
+ pub fn new() -> Parcel {
+ let parcel = unsafe {
+ // Safety: If `AParcel_create` succeeds, it always returns
+ // a valid pointer. If it fails, the process will crash.
+ sys::AParcel_create()
+ };
+ assert!(!parcel.is_null());
+ Self::Owned(parcel)
+ }
+
/// Create a borrowed reference to a parcel object from a raw pointer.
///
/// # Safety
@@ -77,32 +196,21 @@
pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> {
ptr.as_mut().map(|ptr| Self::Borrowed(ptr))
}
+}
- /// Create an owned reference to a parcel object from a raw pointer.
- ///
- /// # Safety
- ///
- /// This constructor is safe if the raw pointer parameter is either null
- /// (resulting in `None`), or a valid pointer to an `AParcel` object. The
- /// parcel object must be owned by the caller prior to this call, as this
- /// constructor takes ownership of the parcel and will destroy it on drop.
- pub(crate) unsafe fn owned(ptr: *mut sys::AParcel) -> Option<Parcel> {
- ptr.as_mut().map(|ptr| Self::Owned(ptr))
+impl Default for Parcel {
+ fn default() -> Self {
+ Self::new()
}
+}
- /// Consume the parcel, transferring ownership to the caller if the parcel
- /// was owned.
- pub(crate) fn into_raw(mut self) -> *mut sys::AParcel {
- let ptr = self.as_native_mut();
- let _ = ManuallyDrop::new(self);
- ptr
- }
-
- pub(crate) fn is_owned(&self) -> bool {
- match *self {
- Self::Owned(_) => true,
- Self::Borrowed(_) => false,
- }
+impl Clone for Parcel {
+ fn clone(&self) -> Self {
+ let mut new_parcel = Self::new();
+ new_parcel
+ .append_all_from(self)
+ .expect("Failed to append from Parcel");
+ new_parcel
}
}
@@ -213,6 +321,30 @@
pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
}
+
+ /// Append a subset of another `Parcel`.
+ ///
+ /// This appends `size` bytes of data from `other` starting at offset
+ /// `start` to the current `Parcel`, or returns an error if not possible.
+ pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> {
+ let status = unsafe {
+ // Safety: `Parcel::appendFrom` from C++ checks that `start`
+ // and `size` are in bounds, and returns an error otherwise.
+ // Both `self` and `other` always contain valid pointers.
+ sys::AParcel_appendFrom(
+ other.as_native(),
+ self.as_native_mut(),
+ start,
+ size,
+ )
+ };
+ status_result(status)
+ }
+
+ /// Append the contents of another `Parcel`.
+ pub fn append_all_from(&mut self, other: &Self) -> Result<()> {
+ self.append_from(other, 0, other.get_data_size())
+ }
}
/// A segment of a writable parcel, used for [`Parcel::sized_write`].
@@ -420,6 +552,18 @@
}
}
+impl Drop for OwnedParcel {
+ fn drop(&mut self) {
+ // Run the C++ Parcel complete object destructor
+ unsafe {
+ // Safety: `OwnedParcel` always contains a valid pointer to an
+ // `AParcel`. Since we own the parcel, we can safely delete it
+ // here.
+ sys::AParcel_delete(self.ptr)
+ }
+ }
+}
+
impl fmt::Debug for Parcel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Parcel")
@@ -427,43 +571,16 @@
}
}
-#[cfg(test)]
-impl Parcel {
- /// Create a new parcel tied to a bogus binder. TESTING ONLY!
- ///
- /// This can only be used for testing! All real parcel operations must be
- /// done in the callback to [`IBinder::transact`] or in
- /// [`Remotable::on_transact`] using the parcels provided to these methods.
- pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> {
- let mut input = ptr::null_mut();
- let status = unsafe {
- // Safety: `SpIBinder` guarantees that `binder` always contains a
- // valid pointer to an `AIBinder`. We pass a valid, mutable out
- // pointer to receive a newly constructed parcel. When successful
- // this function assigns a new pointer to an `AParcel` to `input`
- // and transfers ownership of this pointer to the caller. Thus,
- // after this call, `input` will either be null or point to a valid,
- // owned `AParcel`.
- sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input)
- };
- status_result(status)?;
- unsafe {
- // Safety: `input` is either null or a valid, owned pointer to an
- // `AParcel`, so is valid to safe to
- // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel
- // pointer.
- Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
- }
+impl fmt::Debug for OwnedParcel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("OwnedParcel")
+ .finish()
}
}
#[test]
fn test_read_write() {
- use crate::binder::Interface;
- use crate::native::Binder;
-
- let mut service = Binder::new(()).as_binder();
- let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let mut parcel = Parcel::new();
let start = parcel.get_data_position();
assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA));
@@ -493,11 +610,7 @@
#[test]
#[allow(clippy::float_cmp)]
fn test_read_data() {
- use crate::binder::Interface;
- use crate::native::Binder;
-
- let mut service = Binder::new(()).as_binder();
- let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let mut parcel = Parcel::new();
let str_start = parcel.get_data_position();
parcel.write(&b"Hello, Binder!\0"[..]).unwrap();
@@ -572,11 +685,7 @@
#[test]
fn test_utf8_utf16_conversions() {
- use crate::binder::Interface;
- use crate::native::Binder;
-
- let mut service = Binder::new(()).as_binder();
- let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let mut parcel = Parcel::new();
let start = parcel.get_data_position();
assert!(parcel.write("Hello, Binder!").is_ok());
@@ -636,11 +745,7 @@
#[test]
fn test_sized_write() {
- use crate::binder::Interface;
- use crate::native::Binder;
-
- let mut service = Binder::new(()).as_binder();
- let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let mut parcel = Parcel::new();
let start = parcel.get_data_position();
let arr = [1i32, 2i32, 3i32];
@@ -668,3 +773,43 @@
&arr,
);
}
+
+#[test]
+fn test_append_from() {
+ let mut parcel1 = Parcel::new();
+ parcel1.write(&42i32).expect("Could not perform write");
+
+ let mut parcel2 = Parcel::new();
+ assert_eq!(Ok(()), parcel2.append_all_from(&parcel1));
+ assert_eq!(4, parcel2.get_data_size());
+ assert_eq!(Ok(()), parcel2.append_all_from(&parcel1));
+ assert_eq!(8, parcel2.get_data_size());
+ unsafe {
+ parcel2.set_data_position(0).unwrap();
+ }
+ assert_eq!(Ok(42), parcel2.read::<i32>());
+ assert_eq!(Ok(42), parcel2.read::<i32>());
+
+ let mut parcel2 = Parcel::new();
+ assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2));
+ assert_eq!(Ok(()), parcel2.append_from(&parcel1, 2, 2));
+ assert_eq!(4, parcel2.get_data_size());
+ unsafe {
+ parcel2.set_data_position(0).unwrap();
+ }
+ assert_eq!(Ok(42), parcel2.read::<i32>());
+
+ let mut parcel2 = Parcel::new();
+ assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2));
+ assert_eq!(2, parcel2.get_data_size());
+ unsafe {
+ parcel2.set_data_position(0).unwrap();
+ }
+ assert_eq!(Err(StatusCode::NOT_ENOUGH_DATA), parcel2.read::<i32>());
+
+ let mut parcel2 = Parcel::new();
+ assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 4, 2));
+ assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, 4));
+ assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, -1, 4));
+ assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, -1));
+}
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index f71a686..8bcc5d0 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -94,8 +94,6 @@
}
}
-impl SerializeArray for Option<ParcelFileDescriptor> {}
-
impl DeserializeOption for ParcelFileDescriptor {
fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
let mut fd = -1i32;
@@ -126,8 +124,6 @@
}
}
-impl DeserializeArray for Option<ParcelFileDescriptor> {}
-
impl Deserialize for ParcelFileDescriptor {
fn deserialize(parcel: &Parcel) -> Result<Self> {
Deserialize::deserialize(parcel)
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 56c6165..ec00e1d 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -14,19 +14,42 @@
* limitations under the License.
*/
-use crate::binder::{AsNative, FromIBinder, Strong};
+use crate::binder::{AsNative, FromIBinder, Stability, Strong};
use crate::error::{status_result, status_t, Result, Status, StatusCode};
use crate::parcel::Parcel;
use crate::proxy::SpIBinder;
use crate::sys;
-use std::convert::TryInto;
+use std::convert::{TryFrom, TryInto};
use std::ffi::c_void;
use std::os::raw::{c_char, c_ulong};
use std::mem::{self, MaybeUninit};
use std::ptr;
use std::slice;
+/// Super-trait for Binder parcelables.
+///
+/// This trait is equivalent `android::Parcelable` in C++,
+/// and defines a common interface that all parcelables need
+/// to implement.
+pub trait Parcelable {
+ /// Internal serialization function for parcelables.
+ ///
+ /// This method is mainly for internal use.
+ /// `Serialize::serialize` and its variants are generally
+ /// preferred over this function, since the former also
+ /// prepend a header.
+ fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>;
+
+ /// Internal deserialization function for parcelables.
+ ///
+ /// This method is mainly for internal use.
+ /// `Deserialize::deserialize` and its variants are generally
+ /// preferred over this function, since the former also
+ /// parse the additional header.
+ fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>;
+}
+
/// A struct whose instances can be written to a [`Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Serialize {
@@ -162,6 +185,18 @@
StatusCode::OK as status_t
}
+/// Flag that specifies that the following parcelable is present.
+///
+/// This is the Rust equivalent of `Parcel::kNonNullParcelableFlag`
+/// from `include/binder/Parcel.h` in C++.
+pub const NON_NULL_PARCELABLE_FLAG: i32 = 1;
+
+/// Flag that specifies that the following parcelable is absent.
+///
+/// This is the Rust equivalent of `Parcel::kNullParcelableFlag`
+/// from `include/binder/Parcel.h` in C++.
+pub const NULL_PARCELABLE_FLAG: i32 = 0;
+
/// Helper trait for types that can be nullable when serialized.
// We really need this trait instead of implementing `Serialize for Option<T>`
// because of the Rust orphan rule which prevents us from doing
@@ -173,10 +208,10 @@
/// Serialize an Option of this type into the given [`Parcel`].
fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
if let Some(inner) = this {
- parcel.write(&1i32)?;
+ parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
parcel.write(inner)
} else {
- parcel.write(&0i32)
+ parcel.write(&NULL_PARCELABLE_FLAG)
}
}
}
@@ -186,7 +221,7 @@
/// Deserialize an Option of this type from the given [`Parcel`].
fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
let null: i32 = parcel.read()?;
- if null == 0 {
+ if null == NULL_PARCELABLE_FLAG {
Ok(None)
} else {
parcel.read().map(Some)
@@ -348,6 +383,9 @@
};
}
+impl<T: DeserializeOption> DeserializeArray for Option<T> {}
+impl<T: SerializeOption> SerializeArray for Option<T> {}
+
parcelable_primitives! {
impl Serialize for bool = sys::AParcel_writeBool;
impl Deserialize for bool = sys::AParcel_readBool;
@@ -502,8 +540,6 @@
}
}
-impl SerializeArray for Option<&str> {}
-
impl Serialize for str {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
Some(self).serialize(parcel)
@@ -526,8 +562,6 @@
}
}
-impl SerializeArray for Option<String> {}
-
impl Deserialize for Option<String> {
fn deserialize(parcel: &Parcel) -> Result<Self> {
let mut vec: Option<Vec<u8>> = None;
@@ -608,6 +642,18 @@
}
}
+impl Serialize for Stability {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ i32::from(*self).serialize(parcel)
+ }
+}
+
+impl Deserialize for Stability {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ i32::deserialize(parcel).and_then(Stability::try_from)
+ }
+}
+
impl Serialize for Status {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
unsafe {
@@ -699,19 +745,53 @@
}
}
+/// Implement `Serialize` trait and friends for a parcelable
+///
+/// This is an internal macro used by the AIDL compiler to implement
+/// `Serialize`, `SerializeArray` and `SerializeOption` for
+/// structured parcelables. The target type must implement the
+/// `Parcelable` trait.
+/// ```
+#[macro_export]
+macro_rules! impl_serialize_for_parcelable {
+ ($parcelable:ident) => {
+ impl $crate::parcel::Serialize for $parcelable {
+ fn serialize(
+ &self,
+ parcel: &mut $crate::parcel::Parcel,
+ ) -> $crate::Result<()> {
+ <Self as $crate::parcel::SerializeOption>::serialize_option(
+ Some(self),
+ parcel,
+ )
+ }
+ }
+
+ impl $crate::parcel::SerializeArray for $parcelable {}
+
+ impl $crate::parcel::SerializeOption for $parcelable {
+ fn serialize_option(
+ this: Option<&Self>,
+ parcel: &mut $crate::parcel::Parcel,
+ ) -> $crate::Result<()> {
+ if let Some(this) = this {
+ use $crate::parcel::Parcelable;
+ parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?;
+ this.write_to_parcel(parcel)
+ } else {
+ parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG)
+ }
+ }
+ }
+ }
+}
+
/// Implement `Deserialize` trait and friends for a parcelable
///
/// This is an internal macro used by the AIDL compiler to implement
/// `Deserialize`, `DeserializeArray` and `DeserializeOption` for
-/// structured parcelables. The target type must implement a
-/// `deserialize_parcelable` method with the following signature:
-/// ```no_run
-/// fn deserialize_parcelable(
-/// &mut self,
-/// parcel: &binder::parcel::Parcelable,
-/// ) -> binder::Result<()> {
-/// // ...
-/// }
+/// structured parcelables. The target type must implement the
+/// `Parcelable` trait.
/// ```
#[macro_export]
macro_rules! impl_deserialize_for_parcelable {
@@ -729,10 +809,11 @@
parcel: &$crate::parcel::Parcel,
) -> $crate::Result<()> {
let status: i32 = parcel.read()?;
- if status == 0 {
+ if status == $crate::parcel::NULL_PARCELABLE_FLAG {
Err($crate::StatusCode::UNEXPECTED_NULL)
} else {
- self.deserialize_parcelable(parcel)
+ use $crate::parcel::Parcelable;
+ self.read_from_parcel(parcel)
}
}
}
@@ -752,12 +833,13 @@
parcel: &$crate::parcel::Parcel,
) -> $crate::Result<()> {
let status: i32 = parcel.read()?;
- if status == 0 {
+ if status == $crate::parcel::NULL_PARCELABLE_FLAG {
*this = None;
Ok(())
} else {
+ use $crate::parcel::Parcelable;
this.get_or_insert_with(Self::default)
- .deserialize_parcelable(parcel)
+ .read_from_parcel(parcel)
}
}
}
@@ -790,10 +872,6 @@
#[test]
fn test_custom_parcelable() {
- use crate::binder::Interface;
- use crate::native::Binder;
- let mut service = Binder::new(()).as_binder();
-
struct Custom(u32, bool, String, Vec<String>);
impl Serialize for Custom {
@@ -826,7 +904,7 @@
let custom = Custom(123_456_789, true, string8, strs);
- let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let mut parcel = Parcel::new();
let start = parcel.get_data_position();
assert!(custom.serialize(&mut parcel).is_ok());
@@ -846,13 +924,9 @@
#[test]
#[allow(clippy::excessive_precision)]
fn test_slice_parcelables() {
- use crate::binder::Interface;
- use crate::native::Binder;
- let mut service = Binder::new(()).as_binder();
-
let bools = [true, false, false, true];
- let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let mut parcel = Parcel::new();
let start = parcel.get_data_position();
assert!(bools.serialize(&mut parcel).is_ok());
@@ -876,7 +950,7 @@
let u8s = [101u8, 255, 42, 117];
- let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let mut parcel = Parcel::new();
let start = parcel.get_data_position();
assert!(parcel.write(&u8s[..]).is_ok());
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
new file mode 100644
index 0000000..bccfd2d
--- /dev/null
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::Stability;
+use crate::error::{Result, StatusCode};
+use crate::parcel::{OwnedParcel, Parcel, Parcelable};
+use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
+
+use downcast_rs::{impl_downcast, DowncastSync};
+use std::any::Any;
+use std::sync::{Arc, Mutex};
+
+/// Metadata that `ParcelableHolder` needs for all parcelables.
+///
+/// The compiler auto-generates implementations of this trait
+/// for AIDL parcelables.
+pub trait ParcelableMetadata {
+ /// The Binder parcelable descriptor string.
+ ///
+ /// This string is a unique identifier for a Binder parcelable.
+ fn get_descriptor() -> &'static str;
+
+ /// The Binder parcelable stability.
+ fn get_stability(&self) -> Stability {
+ Stability::Local
+ }
+}
+
+trait AnyParcelable: DowncastSync + Parcelable + std::fmt::Debug {}
+impl_downcast!(sync AnyParcelable);
+impl<T> AnyParcelable for T where T: DowncastSync + Parcelable + std::fmt::Debug {}
+
+#[derive(Debug, Clone)]
+enum ParcelableHolderData {
+ Empty,
+ Parcelable {
+ parcelable: Arc<dyn AnyParcelable>,
+ name: String,
+ },
+ Parcel(OwnedParcel),
+}
+
+impl Default for ParcelableHolderData {
+ fn default() -> Self {
+ ParcelableHolderData::Empty
+ }
+}
+
+/// A container that can hold any arbitrary `Parcelable`.
+///
+/// This type is currently used for AIDL parcelable fields.
+///
+/// `ParcelableHolder` is currently not thread-safe (neither
+/// `Send` nor `Sync`), mainly because it internally contains
+/// a `Parcel` which in turn is not thread-safe.
+#[derive(Debug, Default)]
+pub struct ParcelableHolder {
+ // This is a `Mutex` because of `get_parcelable`
+ // which takes `&self` for consistency with C++.
+ // We could make `get_parcelable` take a `&mut self`
+ // and get rid of the `Mutex` here for a performance
+ // improvement, but then callers would require a mutable
+ // `ParcelableHolder` even for that getter method.
+ data: Mutex<ParcelableHolderData>,
+ stability: Stability,
+}
+
+impl ParcelableHolder {
+ /// Construct a new `ParcelableHolder` with the given stability.
+ pub fn new(stability: Stability) -> Self {
+ Self {
+ data: Mutex::new(ParcelableHolderData::Empty),
+ stability,
+ }
+ }
+
+ /// Reset the contents of this `ParcelableHolder`.
+ ///
+ /// Note that this method does not reset the stability,
+ /// only the contents.
+ pub fn reset(&mut self) {
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Empty;
+ // We could also clear stability here, but C++ doesn't
+ }
+
+ /// Set the parcelable contained in this `ParcelableHolder`.
+ pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<()>
+ where
+ T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
+ {
+ if self.stability > p.get_stability() {
+ return Err(StatusCode::BAD_VALUE);
+ }
+
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Parcelable {
+ parcelable: p,
+ name: T::get_descriptor().into(),
+ };
+
+ Ok(())
+ }
+
+ /// Retrieve the parcelable stored in this `ParcelableHolder`.
+ ///
+ /// This method attempts to retrieve the parcelable inside
+ /// the current object as a parcelable of type `T`.
+ /// The object is validated against `T` by checking that
+ /// its parcelable descriptor matches the one returned
+ /// by `T::get_descriptor()`.
+ ///
+ /// Returns one of the following:
+ /// * `Err(_)` in case of error
+ /// * `Ok(None)` if the holder is empty or the descriptor does not match
+ /// * `Ok(Some(_))` if the object holds a parcelable of type `T`
+ /// with the correct descriptor
+ pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>>
+ where
+ T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync,
+ {
+ let parcelable_desc = T::get_descriptor();
+ let mut data = self.data.lock().unwrap();
+ match *data {
+ ParcelableHolderData::Empty => Ok(None),
+ ParcelableHolderData::Parcelable {
+ ref parcelable,
+ ref name,
+ } => {
+ if name != parcelable_desc {
+ return Err(StatusCode::BAD_VALUE);
+ }
+
+ match Arc::clone(parcelable).downcast_arc::<T>() {
+ Err(_) => Err(StatusCode::BAD_VALUE),
+ Ok(x) => Ok(Some(x)),
+ }
+ }
+ ParcelableHolderData::Parcel(ref mut parcel) => {
+ let parcel = parcel.borrowed();
+ unsafe {
+ // Safety: 0 should always be a valid position.
+ parcel.set_data_position(0)?;
+ }
+
+ let name: String = parcel.read()?;
+ if name != parcelable_desc {
+ return Ok(None);
+ }
+
+ let mut parcelable = T::default();
+ parcelable.read_from_parcel(&parcel)?;
+
+ let parcelable = Arc::new(parcelable);
+ let result = Arc::clone(&parcelable);
+ *data = ParcelableHolderData::Parcelable { parcelable, name };
+
+ Ok(Some(result))
+ }
+ }
+ }
+
+ /// Return the stability value of this object.
+ pub fn get_stability(&self) -> Stability {
+ self.stability
+ }
+}
+
+impl_serialize_for_parcelable!(ParcelableHolder);
+impl_deserialize_for_parcelable!(ParcelableHolder);
+
+impl Parcelable for ParcelableHolder {
+ fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> {
+ parcel.write(&self.stability)?;
+
+ let mut data = self.data.lock().unwrap();
+ match *data {
+ ParcelableHolderData::Empty => parcel.write(&0i32),
+ ParcelableHolderData::Parcelable {
+ ref parcelable,
+ ref name,
+ } => {
+ let length_start = parcel.get_data_position();
+ parcel.write(&0i32)?;
+
+ let data_start = parcel.get_data_position();
+ parcel.write(name)?;
+ parcelable.write_to_parcel(parcel)?;
+
+ let end = parcel.get_data_position();
+ unsafe {
+ // Safety: we got the position from `get_data_position`.
+ parcel.set_data_position(length_start)?;
+ }
+
+ assert!(end >= data_start);
+ parcel.write(&(end - data_start))?;
+ unsafe {
+ // Safety: we got the position from `get_data_position`.
+ parcel.set_data_position(end)?;
+ }
+
+ Ok(())
+ }
+ ParcelableHolderData::Parcel(ref mut p) => {
+ let p = p.borrowed();
+ parcel.write(&p.get_data_size())?;
+ parcel.append_all_from(&p)
+ }
+ }
+ }
+
+ fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> {
+ self.stability = parcel.read()?;
+
+ let data_size: i32 = parcel.read()?;
+ if data_size < 0 {
+ // C++ returns BAD_VALUE here,
+ // while Java returns ILLEGAL_ARGUMENT
+ return Err(StatusCode::BAD_VALUE);
+ }
+ if data_size == 0 {
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Empty;
+ return Ok(());
+ }
+
+ // TODO: C++ ParcelableHolder accepts sizes up to SIZE_MAX here, but we
+ // only go up to i32::MAX because that's what our API uses everywhere
+ let data_start = parcel.get_data_position();
+ let data_end = data_start
+ .checked_add(data_size)
+ .ok_or(StatusCode::BAD_VALUE)?;
+
+ let mut new_parcel = OwnedParcel::new();
+ new_parcel
+ .borrowed()
+ .append_from(parcel, data_start, data_size)?;
+ *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel);
+
+ unsafe {
+ // Safety: `append_from` checks if `data_size` overflows
+ // `parcel` and returns `BAD_VALUE` if that happens. We also
+ // explicitly check for negative and zero `data_size` above,
+ // so `data_end` is guaranteed to be greater than `data_start`.
+ parcel.set_data_position(data_end)?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 68fa34b..a8d0c33 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -22,7 +22,7 @@
};
use crate::error::{status_result, Result, StatusCode};
use crate::parcel::{
- Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ Deserialize, DeserializeArray, DeserializeOption, OwnedParcel, Parcel, Serialize, SerializeArray,
SerializeOption,
};
use crate::sys;
@@ -235,7 +235,7 @@
}
impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
- fn prepare_transact(&self) -> Result<Parcel> {
+ fn prepare_transact(&self) -> Result<OwnedParcel> {
let mut input = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -253,20 +253,19 @@
unsafe {
// Safety: At this point, `input` is either a valid, owned `AParcel`
- // pointer, or null. `Parcel::owned` safely handles both cases,
+ // pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
// taking ownership of the parcel.
- Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ OwnedParcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
fn submit_transact(
&self,
code: TransactionCode,
- data: Parcel,
+ data: OwnedParcel,
flags: TransactionFlags,
- ) -> Result<Parcel> {
+ ) -> Result<OwnedParcel> {
let mut reply = ptr::null_mut();
- assert!(data.is_owned());
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. Although `IBinder::transact` is
@@ -299,9 +298,8 @@
// after the call to `AIBinder_transact` above, so we can
// construct a `Parcel` out of it. `AIBinder_transact` passes
// ownership of the `reply` parcel to Rust, so we need to
- // construct an owned variant. `Parcel::owned` takes ownership
- // of the parcel pointer.
- Parcel::owned(reply).ok_or(StatusCode::UNEXPECTED_NULL)
+ // construct an owned variant.
+ OwnedParcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
@@ -429,7 +427,6 @@
}
impl SerializeArray for SpIBinder {}
-impl SerializeArray for Option<&SpIBinder> {}
impl Deserialize for SpIBinder {
fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
@@ -447,7 +444,6 @@
}
impl DeserializeArray for SpIBinder {}
-impl DeserializeArray for Option<SpIBinder> {}
/// A weak reference to a Binder remote object.
///
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 23e34aa..777f3c9 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -90,7 +90,6 @@
LOG(ERROR) << "Cannot create RpcServer";
return EX_SOFTWARE;
}
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
LOG(ERROR) << "setupInetServer failed: " << statusToString(status);
@@ -207,7 +206,6 @@
service = ServiceManagerProxyToNative::asBinder(interface);
auto rpcServer = RpcServer::make();
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
rpcServer->setRootObject(service);
unsigned int port;
if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 9e10788..fdd02a4 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -18,6 +18,9 @@
oneway void sendString(@utf8InCpp String str);
@utf8InCpp String doubleString(@utf8InCpp String str);
+ // get the port that a client used to connect to this object
+ int getClientPort();
+
// number of known RPC binders to process, RpcState::countBinders by session
int[] countBinders();
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index eec3b44..464da60 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -65,7 +65,9 @@
void initHostRpcServiceManagerOnce() {
static std::once_flag gSmOnce;
- std::call_once(gSmOnce, [] { setDefaultServiceManager(createRpcDelegateServiceManager()); });
+ std::call_once(gSmOnce, [] {
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+ });
}
// Test for host service manager.
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 639876f..4d316f7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -436,6 +436,11 @@
};
};
+TEST_F(BinderLibTest, CannotUseBinderAfterFork) {
+ // EXPECT_DEATH works by forking the process
+ EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork");
+}
+
TEST_F(BinderLibTest, WasParceled) {
auto binder = sp<BBinder>::make();
EXPECT_FALSE(binder->wasParceled());
@@ -1197,7 +1202,6 @@
auto rpcServer = RpcServer::make();
EXPECT_NE(nullptr, rpcServer);
if (rpcServer == nullptr) return {};
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
if (status_t status = rpcServer->setupInetServer("127.0.0.1", 0, &port); status != OK) {
ADD_FAILURE() << "setupInetServer failed" << statusToString(status);
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index f8718aa..52ba9b0 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -206,7 +206,6 @@
if (0 == fork()) {
prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
server->setRootObject(sp<MyBinderRpcBenchmark>::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK_EQ(OK, server->setupUnixDomainServer(addr));
server->join();
exit(1);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 2011801..55ad3c6 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -109,7 +109,6 @@
base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
int sinkFd = sink.get();
auto server = RpcServer::make(newFactory(GetParam()));
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
ASSERT_FALSE(server->hasServer());
ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
ASSERT_TRUE(server->hasServer());
@@ -174,6 +173,7 @@
class MyBinderRpcTest : public BnBinderRpcTest {
public:
wp<RpcServer> server;
+ int port = 0;
Status sendString(const std::string& str) override {
(void)str;
@@ -183,6 +183,10 @@
*strstr = str + str;
return Status::ok();
}
+ Status getClientPort(int* out) override {
+ *out = port;
+ return Status::ok();
+ }
Status countBinders(std::vector<int32_t>* out) override {
sp<RpcServer> spServer = server.promote();
if (spServer == nullptr) {
@@ -481,6 +485,7 @@
size_t numThreads = 1;
size_t numSessions = 1;
size_t numIncomingConnections = 0;
+ size_t numOutgoingConnections = SIZE_MAX;
};
static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
@@ -537,7 +542,6 @@
auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
server->setMaxThreads(options.numThreads);
unsigned int outPort = 0;
@@ -614,6 +618,7 @@
for (const auto& session : sessions) {
session->setMaxIncomingThreads(options.numIncomingConnections);
+ session->setMaxOutgoingThreads(options.numOutgoingConnections);
switch (socketType) {
case SocketType::PRECONNECTED:
@@ -641,13 +646,41 @@
BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) {
BinderRpcTestProcessSession ret{
- .proc = createRpcTestSocketServerProcess(options,
- [&](const sp<RpcServer>& server) {
- sp<MyBinderRpcTest> service =
- new MyBinderRpcTest;
- server->setRootObject(service);
- service->server = server;
- }),
+ .proc = createRpcTestSocketServerProcess(
+ options,
+ [&](const sp<RpcServer>& server) {
+ server->setPerSessionRootObject([&](const sockaddr* addr,
+ socklen_t len) {
+ sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make();
+ switch (addr->sa_family) {
+ case AF_UNIX:
+ // nothing to save
+ break;
+ case AF_VSOCK:
+ CHECK_EQ(len, sizeof(sockaddr_vm));
+ service->port = reinterpret_cast<const sockaddr_vm*>(addr)
+ ->svm_port;
+ break;
+ case AF_INET:
+ CHECK_EQ(len, sizeof(sockaddr_in));
+ service->port =
+ ntohs(reinterpret_cast<const sockaddr_in*>(addr)
+ ->sin_port);
+ break;
+ case AF_INET6:
+ CHECK_EQ(len, sizeof(sockaddr_in));
+ service->port =
+ ntohs(reinterpret_cast<const sockaddr_in6*>(addr)
+ ->sin6_port);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unrecognized address family %d",
+ addr->sa_family);
+ }
+ service->server = server;
+ return service;
+ });
+ }),
};
ret.rootBinder = ret.proc.sessions.at(0).root;
@@ -655,6 +688,9 @@
return ret;
}
+
+ void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
+ size_t sleepMs = 500);
};
TEST_P(BinderRpc, Ping) {
@@ -677,6 +713,27 @@
}
}
+TEST_P(BinderRpc, SeparateRootObject) {
+ SocketType type = std::get<0>(GetParam());
+ if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) {
+ // we can't get port numbers for unix sockets
+ return;
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
+
+ int port1 = 0;
+ EXPECT_OK(proc.rootIface->getClientPort(&port1));
+
+ sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root);
+ int port2;
+ EXPECT_OK(rootIface2->getClientPort(&port2));
+
+ // we should have a different IBinderRpcTest object created for each
+ // session, because we use setPerSessionRootObject
+ EXPECT_NE(port1, port2);
+}
+
TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
auto proc = createRpcTestSocketServerProcess({});
Parcel data;
@@ -685,13 +742,21 @@
}
TEST_P(BinderRpc, AppendSeparateFormats) {
- auto proc = createRpcTestSocketServerProcess({});
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
+
+ Parcel pRaw;
Parcel p1;
- p1.markForBinder(proc.rootBinder);
+ p1.markForBinder(proc1.rootBinder);
p1.writeInt32(3);
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, p1.dataSize()));
+ EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
Parcel p2;
+ p2.markForBinder(proc2.rootBinder);
+ p2.writeInt32(7);
EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
@@ -996,28 +1061,39 @@
for (auto& t : ts) t.join();
}
-TEST_P(BinderRpc, ThreadPoolOverSaturated) {
- constexpr size_t kNumThreads = 10;
- constexpr size_t kNumCalls = kNumThreads + 3;
- constexpr size_t kSleepMs = 500;
-
- auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
-
+void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
+ size_t sleepMs) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
- for (size_t i = 0; i < kNumCalls; i++) {
- ts.push_back(std::thread([&] { proc.rootIface->sleepMs(kSleepMs); }));
+ for (size_t i = 0; i < numCalls; i++) {
+ ts.push_back(std::thread([&] { iface->sleepMs(sleepMs); }));
}
for (auto& t : ts) t.join();
size_t epochMsAfter = epochMillis();
- EXPECT_GE(epochMsAfter, epochMsBefore + 2 * kSleepMs);
+ EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs);
// Potential flake, but make sure calls are handled in parallel.
- EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs);
+ EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs);
+}
+
+TEST_P(BinderRpc, ThreadPoolOverSaturated) {
+ constexpr size_t kNumThreads = 10;
+ constexpr size_t kNumCalls = kNumThreads + 3;
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+}
+
+TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
+ constexpr size_t kNumThreads = 20;
+ constexpr size_t kNumOutgoingConnections = 10;
+ constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
+ auto proc = createRpcTestSocketServerProcess(
+ {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
}
TEST_P(BinderRpc, ThreadingStressTest) {
@@ -1227,11 +1303,20 @@
}
TEST_P(BinderRpc, UseKernelBinderCallingId) {
+ bool okToFork = ProcessState::selfOrNull() == nullptr;
+
auto proc = createRpcTestSocketServerProcess({});
- // we can't allocate IPCThreadState so actually the first time should
- // succeed :(
- EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+ // If this process has used ProcessState already, then the forked process
+ // cannot use it at all. If this process hasn't used it (depending on the
+ // order tests are run), then the forked process can use it, and we'll only
+ // catch the invalid usage the second time. Such is the burden of global
+ // state!
+ if (okToFork) {
+ // we can't allocate IPCThreadState so actually the first time should
+ // succeed :(
+ EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+ }
// second time! we catch the error :)
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
@@ -1283,11 +1368,20 @@
ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
}
+TEST_P(BinderRpc, AidlDelegatorTest) {
+ auto proc = createRpcTestSocketServerProcess({});
+ auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
+ ASSERT_NE(nullptr, myDelegator);
+
+ std::string doubled;
+ EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
static bool testSupportVsockLoopback() {
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
if (status_t status = server->setupVsockServer(vsockPort); status != OK) {
if (status == -EAFNOSUPPORT) {
return false;
@@ -1376,7 +1470,6 @@
TEST_P(BinderRpcSimple, Shutdown) {
auto addr = allocateSocketAddress();
auto server = RpcServer::make(newFactory(GetParam()));
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
auto joinEnds = std::make_shared<OneOffSignal>();
@@ -1416,7 +1509,6 @@
ASSERT_EQ(OK, binder->pingBinder());
auto rpcServer = RpcServer::make();
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
ASSERT_EQ(OK, rpcServer->setupInetServer(kLocalInetAddress, 0, &port));
auto socket = rpcServer->releaseServer();
@@ -1455,7 +1547,6 @@
std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
auto [socketType, rpcSecurity, certificateFormat] = param;
auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
- rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
switch (socketType) {
case SocketType::PRECONNECTED: {
return AssertionFailure() << "Not supported by this test";
@@ -1917,5 +2008,6 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+
return RUN_ALL_TESTS();
}
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index f1fe164..e4f57b0 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -284,7 +284,6 @@
FUZZ_LOG() << "readObject: " << obj;
},
PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
- PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
// additional parcelable objects defined in libbinder
@@ -301,6 +300,14 @@
FUZZ_LOG() << "ParcelableHolder status: " << status;
},
PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
+ [] (const ::android::Parcel& p, uint8_t /* data */) {
+ FUZZ_LOG() << "about to call hasFileDescriptorsInRange() with status";
+ size_t offset = p.readUint32();
+ size_t length = p.readUint32();
+ bool result;
+ status_t status = p.hasFileDescriptorsInRange(offset, length, &result);
+ FUZZ_LOG() << " status: " << status << " result: " << result;
+ },
};
// clang-format on
#pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 6b783a4..c0a762d 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -95,6 +95,11 @@
PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<SomeParcelable>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<std::optional<SomeParcelable>>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<ndk::SpAIBinder>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<ndk::SpAIBinder>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<ndk::ScopedFileDescriptor>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<ndk::ScopedFileDescriptor>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
index c0f0a12..71e847f 100644
--- a/libs/binder/tests/rpc_fuzzer/Android.bp
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -14,6 +14,7 @@
fuzz_config: {
cc: ["smoreland@google.com"],
},
+ corpus: ["corpus/*"],
dictionary: "binder_rpc_fuzzer.dict",
srcs: [
diff --git a/libs/binder/tests/rpc_fuzzer/corpus/special_transaction b/libs/binder/tests/rpc_fuzzer/corpus/special_transaction
new file mode 100644
index 0000000..37228ee
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/corpus/special_transaction
Binary files differ
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index 518849a..a8713a2 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -119,7 +119,6 @@
sp<RpcServer> server = RpcServer::make(makeTransportCtxFactory(&provider));
server->setRootObject(sp<SomeBinder>::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK_EQ(OK, server->setupUnixDomainServer(kSock.c_str()));
std::thread serverThread([=] { (void)server->join(); });
@@ -158,6 +157,8 @@
}
}
+ usleep(10000);
+
if (hangupBeforeShutdown) {
connections.clear();
while (!server->listSessions().empty() || server->numUninitializedSessions()) {
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index 20c5569..e77c55c 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -44,7 +44,6 @@
auto thread = std::thread([&]() {
prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
server->setRootObject(sp<BBinder>::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
server->join();
});
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 64203f7..3190038 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -146,7 +146,6 @@
],
shared_libs: [
- "android.frameworks.bufferhub@1.0",
"libbinder",
"libbufferhub",
"libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
@@ -175,7 +174,6 @@
"BufferHubProducer.cpp",
],
exclude_shared_libs: [
- "android.frameworks.bufferhub@1.0",
"libbufferhub",
"libbufferhubqueue",
"libinput",
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index e3d705f..abcd527 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -147,7 +147,8 @@
OP_ACTIVITY_RECOGNITION_SOURCE = 113,
OP_BLUETOOTH_ADVERTISE = 114,
OP_RECORD_INCOMING_PHONE_AUDIO = 115,
- _NUM_OP = 116
+ OP_NEARBY_WIFI_DEVICES = 116,
+ _NUM_OP = 117
};
AppOpsManager();
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index ae8f238..cc627b8 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -388,7 +388,10 @@
drawBlurLayers(renderengine, display, dstTexture);
}
- // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ // The majority of skia shaders needed by RenderEngine are related to sampling images.
+ // These need to be generated with various source textures.
+ // Make a list of applicable sources.
+ // GRALLOC_USAGE_HW_TEXTURE should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE.
const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
sp<GraphicBuffer> externalBuffer =
new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
@@ -396,24 +399,24 @@
const auto externalTexture =
std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
ExternalTexture::Usage::READABLE);
+ std::vector<const std::shared_ptr<ExternalTexture>> textures =
+ {srcTexture, externalTexture};
- // Another external texture with a different pixel format triggers useIsOpaqueWorkaround
+ // Another external texture with a different pixel format triggers useIsOpaqueWorkaround.
+ // It doesn't have to be f16, but it can't be the usual 8888.
sp<GraphicBuffer> f16ExternalBuffer =
new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_FP16,
1, usageExternal, "primeShaderCache_external_f16");
- const auto f16ExternalTexture =
+ // The F16 texture may not be usable on all devices, so check first that it was created.
+ status_t error = f16ExternalBuffer->initCheck();
+ if (!error) {
+ const auto f16ExternalTexture =
std::make_shared<ExternalTexture>(f16ExternalBuffer, *renderengine,
ExternalTexture::Usage::READABLE);
+ textures.push_back(f16ExternalTexture);
+ }
- // The majority of shaders are related to sampling images.
- // These need to be generated with various source textures
- // The F16 texture may not be usable on all devices, so check first that it was created with
- // the requested usage bit.
- auto textures = {srcTexture, externalTexture};
- auto texturesWithF16 = {srcTexture, externalTexture, f16ExternalTexture};
- bool canUsef16 = f16ExternalBuffer->getUsage() & GRALLOC_USAGE_HW_TEXTURE;
-
- for (auto texture : canUsef16 ? texturesWithF16 : textures) {
+ for (auto texture : textures) {
drawImageLayers(renderengine, display, dstTexture, texture);
// Draw layers for b/185569240.
drawClippedLayers(renderengine, display, dstTexture, texture);
diff --git a/libs/vibrator/OWNERS b/libs/vibrator/OWNERS
index 0997e9f..d073e2b 100644
--- a/libs/vibrator/OWNERS
+++ b/libs/vibrator/OWNERS
@@ -1,2 +1 @@
-lsandrade@google.com
-michaelwr@google.com
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index bba6e22..1be5a96 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -77,7 +77,6 @@
"libsensor",
"libsensorprivacy",
"libpermission",
- "packagemanager_aidl-cpp",
],
}
diff --git a/services/vibratorservice/OWNERS b/services/vibratorservice/OWNERS
new file mode 100644
index 0000000..d073e2b
--- /dev/null
+++ b/services/vibratorservice/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc
index 0de0f9e..1612743 100644
--- a/services/vr/virtual_touchpad/virtual_touchpad.rc
+++ b/services/vr/virtual_touchpad/virtual_touchpad.rc
@@ -2,4 +2,4 @@
class core
user system
group system input uhid
- writepid /dev/cpuset/system/tasks
+ task_profiles VrServiceCapacityNormal