Merge "Add libgui_surfaceComposer_fuzzer"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6fb9a4d..b37a457 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -1195,6 +1195,11 @@
bool traceStream = false;
bool onlyUserspace = false;
+ fprintf(stderr,
+ "** Warning: atrace will end vendor support in the next Android Release. **\n"
+ "** Perfetto is the suggested replacement tool. It will gain vendor **\n"
+ "** support. See https://perfetto.dev/docs/quickstart/android-tracing **\n\n");
+
if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
showHelp(argv[0]);
exit(0);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 942a17e..f759674 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "dumpstate"
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
#include <dirent.h>
#include <errno.h>
@@ -76,6 +77,7 @@
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
+#include <cutils/trace.h>
#include <debuggerd/client.h>
#include <dumpsys.h>
#include <dumputils/dump_utils.h>
@@ -3098,7 +3100,9 @@
TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
// Zip the (now complete) .tmp file within the internal directory.
+ ATRACE_BEGIN("FinalizeFile");
FinalizeFile();
+ ATRACE_END();
// Share the final file with the caller if the user has consented or Shell is the caller.
Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
@@ -3409,6 +3413,9 @@
duration_fd_(duration_fd) {
if (!title_.empty()) {
started_ = Nanotime();
+ if (title_.find("SHOW MAP") == std::string::npos) {
+ ATRACE_ASYNC_BEGIN(title_.c_str(), 0);
+ }
}
}
@@ -3423,6 +3430,9 @@
dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
elapsed, title_.c_str());
}
+ if (title_.find("SHOW MAP") == std::string::npos) {
+ ATRACE_ASYNC_END(title_.c_str(), 0);
+ }
}
}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 45aeab6..4d9b710 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1184,8 +1184,8 @@
int wait_child_with_timeout(pid_t pid, int timeout_ms) {
int pidfd = pidfd_open(pid, /*flags=*/0);
if (pidfd < 0) {
- PLOG(ERROR) << "pidfd_open failed for pid " << pid;
- kill(pid, SIGKILL);
+ PLOG(ERROR) << "pidfd_open failed for pid " << pid
+ << ", waiting for child process without timeout";
return wait_child(pid);
}
diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp
index cbfbdc9..af85666 100644
--- a/cmds/lshal/libprocpartition/Android.bp
+++ b/cmds/lshal/libprocpartition/Android.bp
@@ -35,5 +35,6 @@
],
export_include_dirs: [
"include",
- ]
+ ],
+ min_sdk_version: "30",
}
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 2fb9c2b..1d458b7 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -121,6 +121,8 @@
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
+ LOG(INFO) << "Starting sm instance on " << driver;
+
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
diff --git a/include/input/OWNERS b/include/input/OWNERS
new file mode 100644
index 0000000..c88bfe9
--- /dev/null
+++ b/include/input/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index f2395ba..5e9f540 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -60,27 +60,19 @@
//
// Currently, these are only on system android (not vendor, not host)
// TODO(b/183654927) - move these into separate libraries
-libbinder_device_interface_sources = [
- "IPermissionController.cpp",
- "PermissionCache.cpp",
- "PermissionController.cpp",
-]
-cc_library {
- name: "libbinder",
+filegroup {
+ name: "libbinder_device_interface_sources",
+ srcs: [
+ "IPermissionController.cpp",
+ "PermissionCache.cpp",
+ "PermissionController.cpp",
+ ],
+}
- version_script: "libbinder.map",
-
- // for vndbinder
- vendor_available: true,
- vndk: {
- enabled: true,
- },
- recovery_available: true,
- double_loadable: true,
+cc_defaults {
+ name: "libbinder_defaults",
host_supported: true,
- // TODO(b/153609531): remove when no longer needed.
- native_bridge_supported: true,
// TODO(b/31559095): get headers from bionic on host
include_dirs: [
@@ -88,72 +80,32 @@
"bionic/libc/kernel/uapi/",
],
- // libbinder does not offer a stable wire protocol.
- // if a second copy of it is installed, then it may break after security
- // or dessert updates. Instead, apex users should use libbinder_ndk.
- apex_available: [
- "//apex_available:platform",
- ],
-
srcs: [
"Binder.cpp",
"BpBinder.cpp",
- "BufferedTextOutput.cpp",
"Debug.cpp",
"FdTrigger.cpp",
"IInterface.cpp",
- "IMemory.cpp",
- "IPCThreadState.cpp",
"IResultReceiver.cpp",
- "IServiceManager.cpp",
- "IShellCallback.cpp",
- "LazyServiceRegistrar.cpp",
- "MemoryBase.cpp",
- "MemoryDealer.cpp",
- "MemoryHeapBase.cpp",
+ "OS.cpp",
"Parcel.cpp",
- "ParcelableHolder.cpp",
"ParcelFileDescriptor.cpp",
- "PersistableBundle.cpp",
- "ProcessState.cpp",
"RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
"RpcTransportRaw.cpp",
- "Static.cpp",
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
"Utils.cpp",
- ":libbinder_aidl",
],
target: {
- android: {
- srcs: libbinder_device_interface_sources,
-
- // NOT static to keep the wire protocol unfrozen
- static: {
- enabled: false,
- },
- },
- vendor: {
- exclude_srcs: libbinder_device_interface_sources,
- },
- darwin: {
- enabled: false,
- },
host: {
srcs: [
- "ServiceManagerHost.cpp",
"UtilsHost.cpp",
],
},
- recovery: {
- exclude_header_libs: [
- "libandroid_runtime_vm_headers",
- ],
- },
},
aidl: {
@@ -161,7 +113,6 @@
},
cflags: [
- "-Wall",
"-Wextra",
"-Wextra-semi",
"-Werror",
@@ -224,10 +175,126 @@
"performance*",
"portability*",
],
+}
+
+cc_defaults {
+ name: "libbinder_kernel_defaults",
+ srcs: [
+ "BufferedTextOutput.cpp",
+ "IPCThreadState.cpp",
+ "IServiceManager.cpp",
+ "ProcessState.cpp",
+ "Static.cpp",
+ ":libbinder_aidl",
+ ":libbinder_device_interface_sources",
+ ],
+ target: {
+ vendor: {
+ exclude_srcs: [
+ ":libbinder_device_interface_sources",
+ ],
+ },
+ host: {
+ srcs: [
+ "ServiceManagerHost.cpp",
+ ],
+ },
+ },
+ cflags: [
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+}
+
+cc_library {
+ name: "libbinder",
+ defaults: [
+ "libbinder_defaults",
+ "libbinder_kernel_defaults",
+ ],
+
+ version_script: "libbinder.map",
+
+ // for vndbinder
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ recovery_available: true,
+ double_loadable: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+
+ // libbinder does not offer a stable wire protocol.
+ // if a second copy of it is installed, then it may break after security
+ // or dessert updates. Instead, apex users should use libbinder_ndk.
+ apex_available: [
+ "//apex_available:platform",
+ ],
+
+ srcs: [
+ "IMemory.cpp",
+ "IShellCallback.cpp",
+ "LazyServiceRegistrar.cpp",
+ "MemoryBase.cpp",
+ "MemoryDealer.cpp",
+ "MemoryHeapBase.cpp",
+ "ParcelableHolder.cpp",
+ "PersistableBundle.cpp",
+ ],
+
+ target: {
+ android: {
+ // NOT static to keep the wire protocol unfrozen
+ static: {
+ enabled: false,
+ },
+ },
+ darwin: {
+ enabled: false,
+ },
+ recovery: {
+ exclude_header_libs: [
+ "libandroid_runtime_vm_headers",
+ ],
+ },
+ },
afdo: true,
}
+cc_library_static {
+ name: "libbinder_rpc_no_kernel",
+ defaults: ["libbinder_defaults"],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
+ name: "libbinder_rpc_single_threaded",
+ defaults: [
+ "libbinder_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
+ name: "libbinder_rpc_single_threaded_no_kernel",
+ defaults: ["libbinder_defaults"],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
cc_defaults {
name: "libbinder_tls_shared_deps",
shared_libs: [
@@ -364,6 +431,7 @@
cc_library {
name: "libbatterystats_aidl",
+ host_supported: true,
srcs: [
"IBatteryStats.cpp",
],
@@ -376,6 +444,7 @@
cc_library {
name: "libprocessinfoservice_aidl",
+ host_supported: true,
srcs: [
"IProcessInfoService.cpp",
"ProcessInfoService.cpp",
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index e2db1a3..532bacb 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -38,6 +38,7 @@
#include <linux/sched.h>
#endif
+#include "BuildFlags.h"
#include "RpcState.h"
namespace android {
@@ -164,6 +165,10 @@
ALOGW("setRpcClientDebug disallowed because RPC is not enabled");
return INVALID_OPERATION;
}
+ if (!kEnableKernelIpc) {
+ ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled");
+ return INVALID_OPERATION;
+ }
BBinder* local = this->localBinder();
if (local != nullptr) {
@@ -515,6 +520,10 @@
ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
return INVALID_OPERATION;
}
+ if (!kEnableKernelIpc) {
+ ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled");
+ return INVALID_OPERATION;
+ }
uid_t uid = IPCThreadState::self()->getCallingUid();
if (uid != AID_ROOT) {
ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid);
@@ -540,6 +549,10 @@
ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
return INVALID_OPERATION;
}
+ if (!kEnableKernelIpc) {
+ ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled");
+ return INVALID_OPERATION;
+ }
const int socketFdForPrint = socketFd.get();
LOG_RPC_DETAIL("%s(fd=%d)", __PRETTY_FUNCTION__, socketFdForPrint);
@@ -635,13 +648,14 @@
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(data.readString16());
}
- sp<IShellCallback> shellCallback = IShellCallback::asInterface(
- data.readStrongBinder());
+ sp<IBinder> shellCallbackBinder = data.readStrongBinder();
sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
data.readStrongBinder());
// XXX can't add virtuals until binaries are updated.
- //return shellCommand(in, out, err, args, resultReceiver);
+ // sp<IShellCallback> shellCallback = IShellCallback::asInterface(
+ // shellCallbackBinder);
+ // return shellCommand(in, out, err, args, resultReceiver);
(void)in;
(void)out;
(void)err;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 1eb2ffd..49fc195 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -28,6 +28,8 @@
#include <stdio.h>
+#include "BuildFlags.h"
+
//#undef ALOGV
//#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
@@ -115,6 +117,11 @@
// ---------------------------------------------------------------------------
sp<BpBinder> BpBinder::create(int32_t handle) {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return nullptr;
+ }
+
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
trackedUid = IPCThreadState::self()->getCallingUid();
@@ -177,6 +184,11 @@
}
BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
mTrackedUid = trackedUid;
ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle());
@@ -303,6 +315,11 @@
status = rpcSession()->transact(sp<IBinder>::fromExisting(this), code, data, reply,
flags);
} else {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+ }
+
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) {
@@ -328,6 +345,11 @@
{
if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+ }
+
Obituary ob;
ob.recipient = recipient;
ob.cookie = cookie;
@@ -366,6 +388,11 @@
{
if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+ }
+
AutoMutex _l(mLock);
if (mObitsSent) {
@@ -401,6 +428,11 @@
{
LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder.");
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(),
mObitsSent ? "true" : "false");
@@ -469,12 +501,16 @@
return this;
}
-BpBinder::~BpBinder()
-{
- ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle());
-
+BpBinder::~BpBinder() {
if (CC_UNLIKELY(isRpcBinder())) return;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
+ ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle());
+
IPCThreadState* ipc = IPCThreadState::self();
if (mTrackedUid >= 0) {
@@ -505,21 +541,31 @@
}
}
-void BpBinder::onFirstRef()
-{
- ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle());
+void BpBinder::onFirstRef() {
if (CC_UNLIKELY(isRpcBinder())) return;
+
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
+ ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle());
IPCThreadState* ipc = IPCThreadState::self();
if (ipc) ipc->incStrongHandle(binderHandle(), this);
}
-void BpBinder::onLastStrongRef(const void* /*id*/)
-{
- ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
+void BpBinder::onLastStrongRef(const void* /*id*/) {
if (CC_UNLIKELY(isRpcBinder())) {
(void)rpcSession()->sendDecStrong(this);
return;
}
+
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
+ ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
IF_ALOGV() {
printRefs();
}
@@ -552,6 +598,11 @@
// RPC binder doesn't currently support inc from weak binders
if (CC_UNLIKELY(isRpcBinder())) return false;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return false;
+ }
+
ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle());
IPCThreadState* ipc = IPCThreadState::self();
return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false;
diff --git a/libs/binder/BuildFlags.h b/libs/binder/BuildFlags.h
new file mode 100644
index 0000000..3e9d1c2
--- /dev/null
+++ b/libs/binder/BuildFlags.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace android {
+
+#ifdef BINDER_RPC_SINGLE_THREADED
+constexpr bool kEnableRpcThreads = false;
+#else
+constexpr bool kEnableRpcThreads = true;
+#endif
+
+#ifdef BINDER_WITH_KERNEL_IPC
+constexpr bool kEnableKernelIpc = true;
+#else // BINDER_WITH_KERNEL_IPC
+constexpr bool kEnableKernelIpc = false;
+#endif // BINDER_WITH_KERNEL_IPC
+
+} // namespace android
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index e4ac4b4..c6e4fb3 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -15,6 +15,7 @@
*/
#include "Debug.h"
+#include "BuildFlags.h"
#include <binder/ProcessState.h>
@@ -301,6 +302,11 @@
}
ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return 0;
+ }
+
sp<ProcessState> proc = ProcessState::selfOrNull();
if (proc.get() == nullptr) {
return 0;
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index 5e22593..d123fd1 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -28,25 +28,45 @@
std::unique_ptr<FdTrigger> FdTrigger::make() {
auto ret = std::make_unique<FdTrigger>();
+#ifndef BINDER_RPC_SINGLE_THREADED
if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) {
ALOGE("Could not create pipe %s", strerror(errno));
return nullptr;
}
+#endif
return ret;
}
void FdTrigger::trigger() {
+#ifdef BINDER_RPC_SINGLE_THREADED
+ mTriggered = true;
+#else
mWrite.reset();
+#endif
}
bool FdTrigger::isTriggered() {
+#ifdef BINDER_RPC_SINGLE_THREADED
+ return mTriggered;
+#else
return mWrite == -1;
+#endif
}
status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
+#ifdef BINDER_RPC_SINGLE_THREADED
+ if (mTriggered) {
+ return DEAD_OBJECT;
+ }
+#endif
+
LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", fd.get());
- pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
- {.fd = mRead.get(), .events = 0, .revents = 0}};
+ pollfd pfd[]{
+ {.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
+#ifndef BINDER_RPC_SINGLE_THREADED
+ {.fd = mRead.get(), .events = 0, .revents = 0},
+#endif
+ };
int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
if (ret < 0) {
return -errno;
@@ -55,6 +75,7 @@
// At least one FD has events. Check them.
+#ifndef BINDER_RPC_SINGLE_THREADED
// Detect explicit trigger(): DEAD_OBJECT
if (pfd[1].revents & POLLHUP) {
return DEAD_OBJECT;
@@ -68,6 +89,7 @@
// pfd[1].revents is 0, hence pfd[0].revents must be set, and only possible values are
// a subset of event | POLLHUP | POLLERR | POLLNVAL.
+#endif
// POLLNVAL: invalid FD number, e.g. not opened.
if (pfd[0].revents & POLLNVAL) {
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index a545d6c..5c7102e 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -55,7 +55,11 @@
[[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
private:
+#ifdef BINDER_RPC_SINGLE_THREADED
+ bool mTriggered = false;
+#else
base::unique_fd mWrite;
base::unique_fd mRead;
+#endif
};
} // namespace android
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index fd2d868..fd47783 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -167,7 +167,7 @@
}
}
-#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__)
+#if !defined(__ANDROID_VNDK__)
// IPermissionController is not accessible to vendors
bool checkCallingPermission(const String16& permission)
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp
new file mode 100644
index 0000000..6eb7272
--- /dev/null
+++ b/libs/binder/OS.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OS.h"
+
+#include <android-base/file.h>
+#include <string.h>
+
+using android::base::ErrnoError;
+using android::base::Result;
+
+namespace android {
+
+Result<void> setNonBlocking(android::base::borrowed_fd fd) {
+ int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
+ if (flags == -1) {
+ return ErrnoError() << "Could not get flags for fd";
+ }
+ if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) {
+ return ErrnoError() << "Could not set non-blocking flag for fd";
+ }
+ return {};
+}
+
+status_t getRandomBytes(uint8_t* data, size_t size) {
+ int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (ret == -1) {
+ return -errno;
+ }
+
+ base::unique_fd fd(ret);
+ if (!base::ReadFully(fd, data, size)) {
+ return -errno;
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
new file mode 100644
index 0000000..e802e9c
--- /dev/null
+++ b/libs/binder/OS.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stddef.h>
+#include <cstdint>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
+
+status_t getRandomBytes(uint8_t* data, size_t size);
+
+} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index e67dd7b..537527e 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -20,15 +20,14 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <linux/sched.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/resource.h>
#include <unistd.h>
#include <binder/Binder.h>
@@ -40,6 +39,7 @@
#include <binder/Status.h>
#include <binder/TextOutput.h>
+#include <android-base/scopeguard.h>
#include <cutils/ashmem.h>
#include <cutils/compiler.h>
#include <utils/Flattenable.h>
@@ -51,12 +51,27 @@
#include "RpcState.h"
#include "Static.h"
#include "Utils.h"
+
+// A lot of code in this file uses definitions from the
+// Linux kernel header for Binder <linux/android/binder.h>
+// which is included indirectly via "binder_module.h".
+// Non-Linux OSes do not have that header, so libbinder should be
+// built for those targets without kernel binder support, i.e.,
+// without BINDER_WITH_KERNEL_IPC. For this reason, all code in this
+// file that depends on kernel binder, including the header itself,
+// is conditional on BINDER_WITH_KERNEL_IPC.
+#ifdef BINDER_WITH_KERNEL_IPC
+#include <linux/sched.h>
#include "binder_module.h"
+#else // BINDER_WITH_KERNEL_IPC
+// Needed by {read,write}Pointer
+typedef uintptr_t binder_uintptr_t;
+#endif // BINDER_WITH_KERNEL_IPC
#define LOG_REFS(...)
-//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+// #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOG_ALLOC(...)
-//#define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+// #define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
// ---------------------------------------------------------------------------
@@ -99,6 +114,7 @@
BLOB_ASHMEM_MUTABLE = 2,
};
+#ifdef BINDER_WITH_KERNEL_IPC
static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
const void* who) {
switch (obj.hdr.type) {
@@ -151,6 +167,11 @@
ALOGE("Invalid object type 0x%08x", obj.hdr.type);
}
+#endif // BINDER_WITH_KERNEL_IPC
+
+static int toRawFd(const std::variant<base::unique_fd, base::borrowed_fd>& v) {
+ return std::visit([](const auto& fd) { return fd.get(); }, v);
+}
Parcel::RpcFields::RpcFields(const sp<RpcSession>& session) : mSession(session) {
LOG_ALWAYS_FATAL_IF(mSession == nullptr);
@@ -178,9 +199,11 @@
return OK;
}
+#ifdef BINDER_WITH_KERNEL_IPC
static constexpr inline int schedPolicyMask(int policy, int priority) {
return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT);
}
+#endif // BINDER_WITH_KERNEL_IPC
status_t Parcel::flattenBinder(const sp<IBinder>& binder) {
BBinder* local = nullptr;
@@ -205,6 +228,7 @@
return finishFlattenBinder(binder);
}
+#ifdef BINDER_WITH_KERNEL_IPC
flat_binder_object obj;
int schedBits = 0;
@@ -261,6 +285,10 @@
if (status != OK) return status;
return finishFlattenBinder(binder);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
@@ -290,6 +318,7 @@
return finishUnflattenBinder(binder, out);
}
+#ifdef BINDER_WITH_KERNEL_IPC
const flat_binder_object* flat = readObject(false);
if (flat) {
@@ -307,6 +336,10 @@
}
}
return BAD_TYPE;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
// ---------------------------------------------------------------------------
@@ -471,6 +504,7 @@
err = NO_ERROR;
if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
auto* otherKernelFields = parcel->maybeKernelFields();
LOG_ALWAYS_FATAL_IF(otherKernelFields == nullptr);
@@ -530,6 +564,67 @@
}
}
}
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else {
+ auto* rpcFields = maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+ auto* otherRpcFields = parcel->maybeRpcFields();
+ if (otherRpcFields == nullptr) {
+ return BAD_TYPE;
+ }
+ if (rpcFields->mSession != otherRpcFields->mSession) {
+ return BAD_TYPE;
+ }
+
+ const size_t savedDataPos = mDataPos;
+ base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; };
+
+ rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size());
+ if (otherRpcFields->mFds != nullptr) {
+ if (rpcFields->mFds == nullptr) {
+ rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
+ }
+ rpcFields->mFds->reserve(otherRpcFields->mFds->size());
+ }
+ for (size_t i = 0; i < otherRpcFields->mObjectPositions.size(); i++) {
+ const binder_size_t objPos = otherRpcFields->mObjectPositions[i];
+ if (offset <= objPos && objPos < offset + len) {
+ size_t newDataPos = objPos - offset + startPos;
+ rpcFields->mObjectPositions.push_back(newDataPos);
+
+ mDataPos = newDataPos;
+ int32_t objectType;
+ if (status_t status = readInt32(&objectType); status != OK) {
+ return status;
+ }
+ if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ continue;
+ }
+
+ if (!mAllowFds) {
+ return FDS_NOT_ALLOWED;
+ }
+
+ // Read FD, duplicate, and add to list.
+ int32_t fdIndex;
+ if (status_t status = readInt32(&fdIndex); status != OK) {
+ return status;
+ }
+ const auto& oldFd = otherRpcFields->mFds->at(fdIndex);
+ // To match kernel binder behavior, we always dup, even if the
+ // FD was unowned in the source parcel.
+ rpcFields->mFds->emplace_back(
+ base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0)));
+ // Fixup the index in the data.
+ mDataPos = newDataPos + 4;
+ if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) {
+ return status;
+ }
+ }
+ }
}
return err;
@@ -584,7 +679,7 @@
bool Parcel::hasFileDescriptors() const
{
if (const auto* rpcFields = maybeRpcFields()) {
- return false;
+ return rpcFields->mFds != nullptr && !rpcFields->mFds->empty();
}
auto* kernelFields = maybeKernelFields();
if (!kernelFields->mFdsKnown) {
@@ -596,6 +691,7 @@
std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const {
std::vector<sp<IBinder>> ret;
+#ifdef BINDER_WITH_KERNEL_IPC
const auto* kernelFields = maybeKernelFields();
if (kernelFields == nullptr) {
return ret;
@@ -615,40 +711,43 @@
}
setDataPosition(initPosition);
+#endif // BINDER_WITH_KERNEL_IPC
+
return ret;
}
std::vector<int> Parcel::debugReadAllFileDescriptors() const {
std::vector<int> ret;
- const auto* kernelFields = maybeKernelFields();
- if (kernelFields == nullptr) {
- return ret;
+ if (const auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ size_t initPosition = dataPosition();
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ binder_size_t offset = kernelFields->mObjects[i];
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(mData + offset);
+ if (flat->hdr.type != BINDER_TYPE_FD) continue;
+
+ setDataPosition(offset);
+
+ int fd = readFileDescriptor();
+ LOG_ALWAYS_FATAL_IF(fd == -1);
+ ret.push_back(fd);
+ }
+ setDataPosition(initPosition);
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+#endif
+ } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) {
+ for (const auto& fd : *rpcFields->mFds) {
+ ret.push_back(toRawFd(fd));
+ }
}
- size_t initPosition = dataPosition();
- for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
- binder_size_t offset = kernelFields->mObjects[i];
- const flat_binder_object* flat =
- reinterpret_cast<const flat_binder_object*>(mData + offset);
- if (flat->hdr.type != BINDER_TYPE_FD) continue;
-
- setDataPosition(offset);
-
- int fd = readFileDescriptor();
- LOG_ALWAYS_FATAL_IF(fd == -1);
- ret.push_back(fd);
- }
-
- setDataPosition(initPosition);
return ret;
}
status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const {
- const auto* kernelFields = maybeKernelFields();
- if (kernelFields == nullptr) {
- return BAD_TYPE;
- }
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.
@@ -659,20 +758,38 @@
return BAD_VALUE;
}
*result = false;
- for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
- size_t pos = kernelFields->mObjects[i];
- if (pos < offset) continue;
- if (pos + sizeof(flat_binder_object) > offset + len) {
- if (kernelFields->mObjectsSorted) {
+ if (const auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ size_t pos = kernelFields->mObjects[i];
+ if (pos < offset) continue;
+ if (pos + sizeof(flat_binder_object) > offset + len) {
+ if (kernelFields->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;
- } 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;
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else if (const auto* rpcFields = maybeRpcFields()) {
+ for (uint32_t pos : rpcFields->mObjectPositions) {
+ if (offset <= pos && pos < limit) {
+ const auto* type = reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+ if (*type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ *result = true;
+ break;
+ }
+ }
}
}
return NO_ERROR;
@@ -716,6 +833,7 @@
}
}
+#ifdef BINDER_WITH_KERNEL_IPC
#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#elif defined(__ANDROID_RECOVERY__)
@@ -723,6 +841,7 @@
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
#endif
+#endif // BINDER_WITH_KERNEL_IPC
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
@@ -732,12 +851,17 @@
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid()
: IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
// currently the interface identification token is just its name as a string
@@ -794,6 +918,7 @@
IPCThreadState* threadState) const
{
if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
// StrictModePolicy.
int32_t strictPolicy = readInt32();
if (threadState == nullptr) {
@@ -819,6 +944,11 @@
header);
return false;
}
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)threadState;
+ return false;
+#endif // BINDER_WITH_KERNEL_IPC
}
// Interface descriptor.
@@ -1293,13 +1423,43 @@
return err;
}
-status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
-{
- if (isForRpc()) {
- ALOGE("Cannot write file descriptor to remote binder.");
- return BAD_TYPE;
+status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) {
+ if (auto* rpcFields = maybeRpcFields()) {
+ std::variant<base::unique_fd, base::borrowed_fd> fdVariant;
+ if (takeOwnership) {
+ fdVariant = base::unique_fd(fd);
+ } else {
+ fdVariant = base::borrowed_fd(fd);
+ }
+ if (!mAllowFds) {
+ return FDS_NOT_ALLOWED;
+ }
+ switch (rpcFields->mSession->getFileDescriptorTransportMode()) {
+ case RpcSession::FileDescriptorTransportMode::NONE: {
+ return FDS_NOT_ALLOWED;
+ }
+ case RpcSession::FileDescriptorTransportMode::UNIX: {
+ if (rpcFields->mFds == nullptr) {
+ rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
+ }
+ size_t dataPos = mDataPos;
+ if (dataPos > UINT32_MAX) {
+ return NO_MEMORY;
+ }
+ if (status_t err = writeInt32(RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR); err != OK) {
+ return err;
+ }
+ if (status_t err = writeInt32(rpcFields->mFds->size()); err != OK) {
+ return err;
+ }
+ rpcFields->mObjectPositions.push_back(dataPos);
+ rpcFields->mFds->push_back(std::move(fdVariant));
+ return OK;
+ }
+ }
}
+#ifdef BINDER_WITH_KERNEL_IPC
flat_binder_object obj;
obj.hdr.type = BINDER_TYPE_FD;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
@@ -1307,6 +1467,12 @@
obj.handle = fd;
obj.cookie = takeOwnership ? 1 : 0;
return writeObject(obj, true);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)fd;
+ (void)takeOwnership;
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::writeDupFileDescriptor(int fd)
@@ -1460,6 +1626,7 @@
auto* kernelFields = maybeKernelFields();
LOG_ALWAYS_FATAL_IF(kernelFields == nullptr, "Can't write flat_binder_object to RPC Parcel");
+#ifdef BINDER_WITH_KERNEL_IPC
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity;
if (enoughData && enoughObjects) {
@@ -1502,6 +1669,12 @@
}
goto restart_write;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)val;
+ (void)nullMetaData;
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::writeNoException()
@@ -1519,6 +1692,7 @@
return OK;
}
+#ifdef BINDER_WITH_KERNEL_IPC
// Don't allow non-object reads on object data
if (kernelFields->mObjectsSorted || kernelFields->mObjectsSize <= 1) {
data_sorted:
@@ -1571,6 +1745,10 @@
kernelFields->mNextObjectHint = 0;
kernelFields->mObjectsSorted = true;
goto data_sorted;
+#else // BINDER_WITH_KERNEL_IPC
+ (void)upperBound;
+ return NO_ERROR;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::read(void* outData, size_t len) const
@@ -2038,8 +2216,32 @@
return h;
}
-int Parcel::readFileDescriptor() const
-{
+int Parcel::readFileDescriptor() const {
+ if (const auto* rpcFields = maybeRpcFields()) {
+ if (!std::binary_search(rpcFields->mObjectPositions.begin(),
+ rpcFields->mObjectPositions.end(), mDataPos)) {
+ ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in the "
+ "object list",
+ this, mDataPos);
+ return BAD_TYPE;
+ }
+
+ int32_t objectType = readInt32();
+ if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ return BAD_TYPE;
+ }
+
+ int32_t fdIndex = readInt32();
+ if (rpcFields->mFds == nullptr || fdIndex < 0 ||
+ static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) {
+ ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu",
+ fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0);
+ return BAD_VALUE;
+ }
+ return toRawFd(rpcFields->mFds->at(fdIndex));
+ }
+
+#ifdef BINDER_WITH_KERNEL_IPC
const flat_binder_object* flat = readObject(true);
if (flat && flat->hdr.type == BINDER_TYPE_FD) {
@@ -2047,10 +2249,13 @@
}
return BAD_TYPE;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
-int Parcel::readParcelFileDescriptor() const
-{
+int Parcel::readParcelFileDescriptor() const {
int32_t hasComm = readInt32();
int fd = readFileDescriptor();
if (hasComm != 0) {
@@ -2203,6 +2408,8 @@
return err;
}
+
+#ifdef BINDER_WITH_KERNEL_IPC
const flat_binder_object* Parcel::readObject(bool nullMetaData) const
{
const auto* kernelFields = maybeKernelFields();
@@ -2268,24 +2475,29 @@
}
return nullptr;
}
+#endif // BINDER_WITH_KERNEL_IPC
void Parcel::closeFileDescriptors() {
- auto* kernelFields = maybeKernelFields();
- if (kernelFields == nullptr) {
- return;
- }
- size_t i = kernelFields->mObjectsSize;
- if (i > 0) {
- //ALOGI("Closing file descriptors for %zu objects...", i);
- }
- while (i > 0) {
- i--;
- const flat_binder_object* flat =
- reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
- if (flat->hdr.type == BINDER_TYPE_FD) {
- //ALOGI("Closing fd: %ld", flat->handle);
- close(flat->handle);
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ size_t i = kernelFields->mObjectsSize;
+ if (i > 0) {
+ // ALOGI("Closing file descriptors for %zu objects...", i);
}
+ while (i > 0) {
+ i--;
+ const flat_binder_object* flat =
+ reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ // ALOGI("Closing fd: %ld", flat->handle);
+ close(flat->handle);
+ }
+ }
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+#endif // BINDER_WITH_KERNEL_IPC
+ } else if (auto* rpcFields = maybeRpcFields()) {
+ rpcFields->mFds.reset();
}
}
@@ -2331,6 +2543,7 @@
kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsCount;
mOwner = relFunc;
+#ifdef BINDER_WITH_KERNEL_IPC
binder_size_t minOffset = 0;
for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
binder_size_t offset = kernelFields->mObjects[i];
@@ -2361,10 +2574,17 @@
minOffset = offset + sizeof(flat_binder_object);
}
scanForFds();
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL_IF(objectsCount != 0,
+ "Non-zero objects count passed to Parcel with kernel driver disabled");
+#endif // BINDER_WITH_KERNEL_IPC
}
-void Parcel::rpcSetDataReference(const sp<RpcSession>& session, const uint8_t* data,
- size_t dataSize, release_func relFunc) {
+status_t Parcel::rpcSetDataReference(
+ const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
+ const uint32_t* objectTable, size_t objectTableSize,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds,
+ release_func relFunc) {
// this code uses 'mOwner == nullptr' to understand whether it owns memory
LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
@@ -2373,9 +2593,29 @@
freeData();
markForRpc(session);
+ auto* rpcFields = maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc.
+
mData = const_cast<uint8_t*>(data);
mDataSize = mDataCapacity = dataSize;
mOwner = relFunc;
+
+ if (objectTableSize != ancillaryFds.size()) {
+ ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size());
+ freeData(); // don't leak mData
+ return BAD_VALUE;
+ }
+
+ rpcFields->mObjectPositions.reserve(objectTableSize);
+ for (size_t i = 0; i < objectTableSize; i++) {
+ rpcFields->mObjectPositions.push_back(objectTable[i]);
+ }
+ if (!ancillaryFds.empty()) {
+ rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
+ *rpcFields->mFds = std::move(ancillaryFds);
+ }
+
+ return OK;
}
void Parcel::print(TextOutput& to, uint32_t /*flags*/) const
@@ -2388,6 +2628,7 @@
} else if (dataSize() > 0) {
const uint8_t* DATA = data();
to << indent << HexDump(DATA, dataSize()) << dedent;
+#ifdef BINDER_WITH_KERNEL_IPC
if (const auto* kernelFields = maybeKernelFields()) {
const binder_size_t* OBJS = kernelFields->mObjects;
const size_t N = objectsCount();
@@ -2399,6 +2640,7 @@
<< TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder;
}
}
+#endif // BINDER_WITH_KERNEL_IPC
} else {
to << "NULL";
}
@@ -2413,6 +2655,7 @@
return;
}
+#ifdef BINDER_WITH_KERNEL_IPC
size_t i = kernelFields->mObjectsSize;
if (i == 0) {
return;
@@ -2425,6 +2668,7 @@
const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
release_object(proc, *flat, this);
}
+#endif // BINDER_WITH_KERNEL_IPC
}
void Parcel::acquireObjects()
@@ -2434,6 +2678,7 @@
return;
}
+#ifdef BINDER_WITH_KERNEL_IPC
size_t i = kernelFields->mObjectsSize;
if (i == 0) {
return;
@@ -2446,6 +2691,7 @@
const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
acquire_object(proc, *flat, this);
}
+#endif // BINDER_WITH_KERNEL_IPC
}
void Parcel::freeData()
@@ -2558,6 +2804,9 @@
kernelFields->mObjectsSorted = false;
kernelFields->mHasFds = false;
kernelFields->mFdsKnown = true;
+ } else if (auto* rpcFields = maybeRpcFields()) {
+ rpcFields->mObjectPositions.clear();
+ rpcFields->mFds.reset();
}
mAllowFds = true;
@@ -2573,17 +2822,26 @@
}
auto* kernelFields = maybeKernelFields();
+ auto* rpcFields = maybeRpcFields();
// If shrinking, first adjust for any objects that appear
// after the new data size.
- size_t objectsSize = kernelFields ? kernelFields->mObjectsSize : 0;
- if (kernelFields && desired < mDataSize) {
+ size_t objectsSize =
+ kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size();
+ if (desired < mDataSize) {
if (desired == 0) {
objectsSize = 0;
} else {
- while (objectsSize > 0) {
- if (kernelFields->mObjects[objectsSize - 1] < desired) break;
- objectsSize--;
+ if (kernelFields) {
+ while (objectsSize > 0) {
+ if (kernelFields->mObjects[objectsSize - 1] < desired) break;
+ objectsSize--;
+ }
+ } else {
+ while (objectsSize > 0) {
+ if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
+ objectsSize--;
+ }
}
}
}
@@ -2604,7 +2862,7 @@
}
binder_size_t* objects = nullptr;
- if (objectsSize) {
+ if (kernelFields && objectsSize) {
objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
if (!objects) {
free(data);
@@ -2620,6 +2878,12 @@
acquireObjects();
kernelFields->mObjectsSize = oldObjectsSize;
}
+ if (rpcFields) {
+ if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
+ free(data);
+ return status;
+ }
+ }
if (mData) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
@@ -2649,6 +2913,7 @@
} else if (mData) {
if (kernelFields && objectsSize < kernelFields->mObjectsSize) {
+#ifdef BINDER_WITH_KERNEL_IPC
// Need to release refs on any objects we are dropping.
const sp<ProcessState> proc(ProcessState::self());
for (size_t i = objectsSize; i < kernelFields->mObjectsSize; i++) {
@@ -2677,6 +2942,14 @@
kernelFields->mObjectsSize = objectsSize;
kernelFields->mNextObjectHint = 0;
kernelFields->mObjectsSorted = false;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Non-zero numObjects for RPC Parcel");
+#endif // BINDER_WITH_KERNEL_IPC
+ }
+ if (rpcFields) {
+ if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
+ return status;
+ }
}
// We own the data, so we can just do a realloc().
@@ -2734,6 +3007,35 @@
return NO_ERROR;
}
+status_t Parcel::truncateRpcObjects(size_t newObjectsSize) {
+ auto* rpcFields = maybeRpcFields();
+ if (newObjectsSize == 0) {
+ rpcFields->mObjectPositions.clear();
+ if (rpcFields->mFds) {
+ rpcFields->mFds->clear();
+ }
+ return OK;
+ }
+ while (rpcFields->mObjectPositions.size() > newObjectsSize) {
+ uint32_t pos = rpcFields->mObjectPositions.back();
+ rpcFields->mObjectPositions.pop_back();
+ const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+ if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ const auto fdIndex =
+ *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType));
+ if (rpcFields->mFds == nullptr || fdIndex < 0 ||
+ static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) {
+ ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu",
+ fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0);
+ return BAD_VALUE;
+ }
+ // In practice, this always removes the last element.
+ rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex);
+ }
+ }
+ return OK;
+}
+
void Parcel::initState()
{
LOG_ALLOC("Parcel %p: initState", this);
@@ -2760,6 +3062,7 @@
kernelFields->mFdsKnown = true;
}
+#ifdef BINDER_WITH_KERNEL_IPC
size_t Parcel::getBlobAshmemSize() const
{
// This used to return the size of all blobs that were written to ashmem, now we're returning
@@ -2792,6 +3095,7 @@
}
return openAshmemSize;
}
+#endif // BINDER_WITH_KERNEL_IPC
// --- Parcel::Blob ---
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 528341e..096d5cc 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -32,7 +32,9 @@
#include <log/log.h>
#include <utils/Compat.h>
+#include "BuildFlags.h"
#include "FdTrigger.h"
+#include "OS.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
#include "RpcWireFormat.h"
@@ -122,28 +124,36 @@
mProtocolVersion = version;
}
+void RpcServer::setSupportedFileDescriptorTransportModes(
+ const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
+ mSupportedFileDescriptorTransportModes.reset();
+ for (RpcSession::FileDescriptorTransportMode mode : modes) {
+ mSupportedFileDescriptorTransportModes.set(static_cast<size_t>(mode));
+ }
+}
+
void RpcServer::setRootObject(const sp<IBinder>& binder) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
mRootObjectFactory = nullptr;
mRootObjectWeak = mRootObject = binder;
}
void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
mRootObject.clear();
mRootObjectFactory = nullptr;
mRootObjectWeak = binder;
}
void RpcServer::setPerSessionRootObject(
std::function<sp<IBinder>(const void*, size_t)>&& makeObject) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
mRootObject.clear();
mRootObjectWeak.clear();
mRootObjectFactory = std::move(makeObject);
}
sp<IBinder> RpcServer::getRootObject() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
bool hasWeak = mRootObjectWeak.unsafe_get();
sp<IBinder> ret = mRootObjectWeak.promote();
ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr");
@@ -151,7 +161,7 @@
}
std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return mCtx->getCertificate(format);
}
@@ -160,15 +170,17 @@
}
void RpcServer::start() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!");
- mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
+ mJoinThread =
+ std::make_unique<RpcMaybeThread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
+ rpcJoinIfSingleThreaded(*mJoinThread);
}
void RpcServer::join() {
{
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join.");
LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
mJoinThreadRunning = true;
@@ -196,24 +208,31 @@
LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
{
- std::lock_guard<std::mutex> _l(mLock);
- std::thread thread =
- std::thread(&RpcServer::establishConnection, sp<RpcServer>::fromExisting(this),
- std::move(clientFd), addr, addrLen);
- mConnectingThreads[thread.get_id()] = std::move(thread);
+ RpcMutexLockGuard _l(mLock);
+ RpcMaybeThread thread = RpcMaybeThread(&RpcServer::establishConnection,
+ sp<RpcServer>::fromExisting(this),
+ std::move(clientFd), addr, addrLen);
+
+ auto& threadRef = mConnectingThreads[thread.get_id()];
+ threadRef = std::move(thread);
+ rpcJoinIfSingleThreaded(threadRef);
}
}
LOG_RPC_DETAIL("RpcServer::join exiting with %s", statusToString(status).c_str());
- {
- std::lock_guard<std::mutex> _l(mLock);
+ if constexpr (kEnableRpcThreads) {
+ RpcMutexLockGuard _l(mLock);
mJoinThreadRunning = false;
+ } else {
+ // Multi-threaded builds clear this in shutdown(), but we need it valid
+ // so the loop above exits cleanly
+ mShutdownTrigger = nullptr;
}
mShutdownCv.notify_all();
}
bool RpcServer::shutdown() {
- std::unique_lock<std::mutex> _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
if (mShutdownTrigger == nullptr) {
LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown?)");
return false;
@@ -224,10 +243,16 @@
for (auto& [id, session] : mSessions) {
(void)id;
// server lock is a more general lock
- std::lock_guard<std::mutex> _lSession(session->mMutex);
+ RpcMutexLockGuard _lSession(session->mMutex);
session->mShutdownTrigger->trigger();
}
+ if constexpr (!kEnableRpcThreads) {
+ // In single-threaded mode we're done here, everything else that
+ // needs to happen should be at the end of RpcServer::join()
+ return true;
+ }
+
while (mJoinThreadRunning || !mConnectingThreads.empty() || !mSessions.empty()) {
if (std::cv_status::timeout == mShutdownCv.wait_for(_l, std::chrono::seconds(1))) {
ALOGE("Waiting for RpcServer to shut down (1s w/o progress). Join thread running: %d, "
@@ -255,7 +280,7 @@
}
std::vector<sp<RpcSession>> RpcServer::listSessions() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
std::vector<sp<RpcSession>> sessions;
for (auto& [id, session] : mSessions) {
(void)id;
@@ -265,7 +290,7 @@
}
size_t RpcServer::numUninitializedSessions() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return mConnectingThreads.size();
}
@@ -292,7 +317,7 @@
if (status == OK) {
iovec iov{&header, sizeof(header)};
status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1,
- std::nullopt);
+ std::nullopt, /*ancillaryFds=*/nullptr);
if (status != OK) {
ALOGE("Failed to read ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -307,7 +332,7 @@
sessionId.resize(header.sessionIdSize);
iovec iov{sessionId.data(), sessionId.size()};
status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1,
- std::nullopt);
+ std::nullopt, /*ancillaryFds=*/nullptr);
if (status != OK) {
ALOGE("Failed to read session ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -338,7 +363,7 @@
iovec iov{&response, sizeof(response)};
status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1,
- std::nullopt);
+ std::nullopt, nullptr);
if (status != OK) {
ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
// still need to cleanup before we can return
@@ -346,12 +371,12 @@
}
}
- std::thread thisThread;
+ RpcMaybeThread thisThread;
sp<RpcSession> session;
{
- std::unique_lock<std::mutex> _l(server->mLock);
+ RpcMutexUniqueLock _l(server->mLock);
- auto threadId = server->mConnectingThreads.find(std::this_thread::get_id());
+ auto threadId = server->mConnectingThreads.find(rpc_this_thread::get_id());
LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(),
"Must establish connection on owned thread");
thisThread = std::move(threadId->second);
@@ -396,6 +421,19 @@
session->setMaxIncomingThreads(server->mMaxThreads);
if (!session->setProtocolVersion(protocolVersion)) return;
+ if (header.fileDescriptorTransportMode <
+ server->mSupportedFileDescriptorTransportModes.size() &&
+ server->mSupportedFileDescriptorTransportModes.test(
+ header.fileDescriptorTransportMode)) {
+ session->setFileDescriptorTransportMode(
+ static_cast<RpcSession::FileDescriptorTransportMode>(
+ header.fileDescriptorTransportMode));
+ } else {
+ ALOGE("Rejecting connection: FileDescriptorTransportMode is not supported: %hhu",
+ header.fileDescriptorTransportMode);
+ return;
+ }
+
// if null, falls back to server root
sp<IBinder> sessionSpecificRoot;
if (server->mRootObjectFactory != nullptr) {
@@ -486,7 +524,7 @@
LOG_RPC_DETAIL("Dropping session with address %s",
base::HexString(id.data(), id.size()).c_str());
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
auto it = mSessions.find(id);
LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %s",
base::HexString(id.data(), id.size()).c_str());
@@ -500,17 +538,17 @@
}
bool RpcServer::hasServer() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return mServer.ok();
}
unique_fd RpcServer::releaseServer() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return std::move(mServer);
}
status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
if (mServer.ok()) {
ALOGE("Each RpcServer can only have one server.");
return INVALID_OPERATION;
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 8edc78f..80f6a37 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -21,7 +21,6 @@
#include <dlfcn.h>
#include <inttypes.h>
#include <poll.h>
-#include <pthread.h>
#include <unistd.h>
#include <string_view>
@@ -38,6 +37,7 @@
#include <utils/String8.h>
#include "FdTrigger.h"
+#include "OS.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
#include "RpcWireFormat.h"
@@ -60,7 +60,7 @@
RpcSession::~RpcSession() {
LOG_RPC_DETAIL("RpcSession destroyed %p", this);
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mConnections.mIncoming.size() != 0,
"Should not be able to destroy a session with servers in use.");
}
@@ -77,7 +77,7 @@
}
void RpcSession::setMaxIncomingThreads(size_t threads) {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
"Must set max incoming threads before setting up connections, but has %zu "
"client(s) and %zu server(s)",
@@ -86,12 +86,12 @@
}
size_t RpcSession::getMaxIncomingThreads() {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
return mMaxIncomingThreads;
}
void RpcSession::setMaxOutgoingThreads(size_t threads) {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _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)",
@@ -100,7 +100,7 @@
}
size_t RpcSession::getMaxOutgoingThreads() {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
return mMaxOutgoingThreads;
}
@@ -113,7 +113,7 @@
return false;
}
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
if (mProtocolVersion && version > *mProtocolVersion) {
ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u",
*mProtocolVersion, version);
@@ -125,10 +125,18 @@
}
std::optional<uint32_t> RpcSession::getProtocolVersion() {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
return mProtocolVersion;
}
+void RpcSession::setFileDescriptorTransportMode(FileDescriptorTransportMode mode) {
+ mFileDescriptorTransportMode = mode;
+}
+
+RpcSession::FileDescriptorTransportMode RpcSession::getFileDescriptorTransportMode() {
+ return mFileDescriptorTransportMode;
+}
+
status_t RpcSession::setupUnixDomainClient(const char* path) {
return setupSocketClient(UnixSocketAddress(path));
}
@@ -201,7 +209,7 @@
}
bool RpcSession::shutdownAndWait(bool wait) {
- std::unique_lock<std::mutex> _l(mMutex);
+ RpcMutexUniqueLock _l(mMutex);
LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Shutdown trigger not installed");
mShutdownTrigger->trigger();
@@ -214,6 +222,7 @@
}
_l.unlock();
+
mRpcBinderState->clear();
return true;
@@ -248,7 +257,7 @@
status_t RpcSession::readId() {
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
}
@@ -274,7 +283,7 @@
mCv.notify_all();
}
-void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock,
+void RpcSession::WaitForShutdownListener::waitForShutdown(RpcMutexUniqueLock& lock,
const sp<RpcSession>& session) {
while (session->mConnections.mIncoming.size() > 0) {
if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) {
@@ -285,11 +294,11 @@
}
}
-void RpcSession::preJoinThreadOwnership(std::thread thread) {
- LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread");
+void RpcSession::preJoinThreadOwnership(RpcMaybeThread thread) {
+ LOG_ALWAYS_FATAL_IF(thread.get_id() != rpc_this_thread::get_id(), "Must own this thread");
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
mConnections.mThreads[thread.get_id()] = std::move(thread);
}
}
@@ -396,8 +405,8 @@
sp<RpcSession::EventListener> listener;
{
- std::lock_guard<std::mutex> _l(session->mMutex);
- auto it = session->mConnections.mThreads.find(std::this_thread::get_id());
+ RpcMutexLockGuard _l(session->mMutex);
+ auto it = session->mConnections.mThreads.find(rpc_this_thread::get_id());
LOG_ALWAYS_FATAL_IF(it == session->mConnections.mThreads.end());
it->second.detach();
session->mConnections.mThreads.erase(it);
@@ -430,7 +439,7 @@
status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
bool incoming)>& connectAndInit) {
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mConnections.mOutgoing.size() != 0,
"Must only setup session once, but already has %zu clients",
mConnections.mOutgoing.size());
@@ -492,7 +501,11 @@
return status;
}
+#ifdef BINDER_RPC_SINGLE_THREADED
+ constexpr size_t outgoingThreads = 1;
+#else // BINDER_RPC_SINGLE_THREADED
size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
+#endif // BINDER_RPC_SINGLE_THREADED
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.",
@@ -606,6 +619,7 @@
RpcConnectionHeader header{
.version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
.options = 0,
+ .fileDescriptorTransportMode = static_cast<uint8_t>(mFileDescriptorTransportMode),
.sessionIdSize = static_cast<uint16_t>(sessionId.size()),
};
@@ -614,8 +628,8 @@
}
iovec headerIov{&header, sizeof(header)};
- auto sendHeaderStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, std::nullopt);
+ auto sendHeaderStatus = server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1,
+ std::nullopt, nullptr);
if (sendHeaderStatus != OK) {
ALOGE("Could not write connection header to socket: %s",
statusToString(sendHeaderStatus).c_str());
@@ -625,8 +639,9 @@
if (sessionId.size() > 0) {
iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())),
sessionId.size()};
- auto sendSessionIdStatus = server->interruptableWriteFully(mShutdownTrigger.get(),
- &sessionIov, 1, std::nullopt);
+ auto sendSessionIdStatus =
+ server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1,
+ std::nullopt, nullptr);
if (sendSessionIdStatus != OK) {
ALOGE("Could not write session ID ('%s') to socket: %s",
base::HexString(sessionId.data(), sessionId.size()).c_str(),
@@ -645,14 +660,14 @@
}
status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport) {
- std::mutex mutex;
- std::condition_variable joinCv;
- std::unique_lock<std::mutex> lock(mutex);
- std::thread thread;
+ RpcMutex mutex;
+ RpcConditionVariable joinCv;
+ RpcMutexUniqueLock lock(mutex);
+ RpcMaybeThread thread;
sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this);
bool ownershipTransferred = false;
- thread = std::thread([&]() {
- std::unique_lock<std::mutex> threadLock(mutex);
+ thread = RpcMaybeThread([&]() {
+ RpcMutexUniqueLock threadLock(mutex);
std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport);
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
sp<RpcSession> session = thiz;
@@ -668,6 +683,7 @@
RpcSession::join(std::move(session), std::move(setupResult));
});
+ rpcJoinIfSingleThreaded(thread);
joinCv.wait(lock, [&] { return ownershipTransferred; });
LOG_ALWAYS_FATAL_IF(!ownershipTransferred);
return OK;
@@ -687,9 +703,9 @@
status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) {
sp<RpcConnection> connection = sp<RpcConnection>::make();
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
connection->rpcTransport = std::move(rpcTransport);
- connection->exclusiveTid = base::GetThreadId();
+ connection->exclusiveTid = rpcGetThreadId();
mConnections.mOutgoing.push_back(connection);
}
@@ -699,10 +715,7 @@
mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this));
}
- {
- std::lock_guard<std::mutex> _l(mMutex);
- connection->exclusiveTid = std::nullopt;
- }
+ clearConnectionTid(connection);
return status;
}
@@ -729,7 +742,7 @@
sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(
std::unique_ptr<RpcTransport> rpcTransport) {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
if (mConnections.mIncoming.size() >= mMaxIncomingThreads) {
ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)",
@@ -747,7 +760,7 @@
sp<RpcConnection> session = sp<RpcConnection>::make();
session->rpcTransport = std::move(rpcTransport);
- session->exclusiveTid = base::GetThreadId();
+ session->exclusiveTid = rpcGetThreadId();
mConnections.mIncoming.push_back(session);
mConnections.mMaxIncoming = mConnections.mIncoming.size();
@@ -756,7 +769,7 @@
}
bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) {
- std::unique_lock<std::mutex> _l(mMutex);
+ RpcMutexUniqueLock _l(mMutex);
if (auto it =
std::find(mConnections.mIncoming.begin(), mConnections.mIncoming.end(), connection);
it != mConnections.mIncoming.end()) {
@@ -773,6 +786,15 @@
return false;
}
+void RpcSession::clearConnectionTid(const sp<RpcConnection>& connection) {
+ RpcMutexUniqueLock _l(mMutex);
+ connection->exclusiveTid = std::nullopt;
+ if (mConnections.mWaitingThreads > 0) {
+ _l.unlock();
+ mAvailableConnectionCv.notify_one();
+ }
+}
+
std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) {
return mCtx->getCertificate(format);
}
@@ -783,8 +805,8 @@
connection->mConnection = nullptr;
connection->mReentrant = false;
- uint64_t tid = base::GetThreadId();
- std::unique_lock<std::mutex> _l(session->mMutex);
+ uint64_t tid = rpcGetThreadId();
+ RpcMutexUniqueLock _l(session->mMutex);
session->mConnections.mWaitingThreads++;
while (true) {
@@ -902,12 +924,7 @@
// is using this fd, and it retains the right to it. So, we don't give up
// exclusive ownership, and no thread is freed.
if (!mReentrant && mConnection != nullptr) {
- std::unique_lock<std::mutex> _l(mSession->mMutex);
- mConnection->exclusiveTid = std::nullopt;
- if (mSession->mConnections.mWaitingThreads > 0) {
- _l.unlock();
- mSession->mAvailableConnectionCv.notify_one();
- }
+ mSession->clearConnectionTid(mConnection);
}
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 419df86..0ae75cd 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -21,6 +21,7 @@
#include <android-base/hex.h>
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/RpcServer.h>
@@ -35,7 +36,7 @@
namespace android {
-using base::ScopeGuard;
+using base::StringPrintf;
#if RPC_FLAKE_PRONE
void rpcMaybeWaitToFlake() {
@@ -43,13 +44,22 @@
[[clang::no_destroy]] static std::mutex m;
unsigned num;
{
- std::lock_guard<std::mutex> lock(m);
+ RpcMutexLockGuard lock(m);
num = r();
}
if (num % 10 == 0) usleep(num % 1000);
}
#endif
+static bool enableAncillaryFds(RpcSession::FileDescriptorTransportMode mode) {
+ switch (mode) {
+ case RpcSession::FileDescriptorTransportMode::NONE:
+ return false;
+ case RpcSession::FileDescriptorTransportMode::UNIX:
+ return true;
+ }
+}
+
RpcState::RpcState() {}
RpcState::~RpcState() {}
@@ -78,7 +88,7 @@
return INVALID_OPERATION;
}
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT;
// TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map
@@ -154,7 +164,7 @@
return BAD_VALUE;
}
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT;
if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
@@ -189,7 +199,7 @@
// extra reference counting packets now.
if (binder->remoteBinder()) return OK;
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT;
auto it = mNodeForAddress.find(address);
@@ -217,17 +227,17 @@
}
size_t RpcState::countBinders() {
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
return mNodeForAddress.size();
}
void RpcState::dump() {
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
dumpLocked();
}
void RpcState::clear() {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) {
LOG_ALWAYS_FATAL_IF(!mNodeForAddress.empty(),
@@ -310,9 +320,11 @@
mData.reset(new (std::nothrow) uint8_t[size]);
}
-status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+status_t RpcState::rpcSend(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
for (int i = 0; i < niovs; i++) {
LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s",
what, i + 1, niovs, connection->rpcTransport.get(),
@@ -321,7 +333,8 @@
if (status_t status =
connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
- iovs, niovs, altPoll);
+ iovs, niovs, altPoll,
+ ancillaryFds);
status != OK) {
LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -332,11 +345,14 @@
return OK;
}
-status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) {
+status_t RpcState::rpcRec(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
if (status_t status =
connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
- iovs, niovs, std::nullopt);
+ iovs, niovs, std::nullopt,
+ ancillaryFds);
status != OK) {
LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -356,7 +372,7 @@
const sp<RpcSession>& session, uint32_t* version) {
RpcNewSessionResponse response;
iovec iov{&response, sizeof(response)};
- if (status_t status = rpcRec(connection, session, "new session response", &iov, 1);
+ if (status_t status = rpcRec(connection, session, "new session response", &iov, 1, nullptr);
status != OK) {
return status;
}
@@ -377,7 +393,8 @@
const sp<RpcSession>& session) {
RpcOutgoingConnectionInit init;
iovec iov{&init, sizeof(init)};
- if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK)
+ if (status_t status = rpcRec(connection, session, "connection init", &iov, 1, nullptr);
+ status != OK)
return status;
static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY));
@@ -449,20 +466,12 @@
status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection,
const sp<IBinder>& binder, uint32_t code, const Parcel& data,
const sp<RpcSession>& session, Parcel* reply, uint32_t flags) {
- if (!data.isForRpc()) {
- ALOGE("Refusing to send RPC with parcel not crafted for RPC call on binder %p code "
- "%" PRIu32,
- binder.get(), code);
- return BAD_TYPE;
+ std::string errorMsg;
+ if (status_t status = validateParcel(session, data, &errorMsg); status != OK) {
+ ALOGE("Refusing to send RPC on binder %p code %" PRIu32 ": Parcel %p failed validation: %s",
+ binder.get(), code, &data, errorMsg.c_str());
+ return status;
}
-
- if (data.objectsCount() != 0) {
- ALOGE("Parcel at %p has attached objects but is being used in an RPC call on binder %p "
- "code %" PRIu32,
- &data, binder.get(), code);
- return BAD_TYPE;
- }
-
uint64_t address;
if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status;
@@ -478,7 +487,7 @@
uint64_t asyncNumber = 0;
if (address != 0) {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(address);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -494,9 +503,11 @@
}
}
- // objectTable always empty for now. Will be populated from `data` soon.
- std::vector<uint32_t> objectTable;
- Span<const uint32_t> objectTableSpan = {objectTable.data(), objectTable.size()};
+ auto* rpcFields = data.maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+
+ Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(),
+ rpcFields->mObjectPositions.size()};
uint32_t bodySize;
LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireTransaction), data.dataSize(),
@@ -532,25 +543,25 @@
{const_cast<uint8_t*>(data.data()), data.dataSize()},
objectTableSpan.toIovec(),
};
- if (status_t status = rpcSend(connection, session, "transaction", iovs, arraysize(iovs),
- [&] {
- if (waitUs > kWaitLogUs) {
- ALOGE("Cannot send command, trying to process pending "
- "refcounts. Waiting %zuus. Too "
- "many oneway calls?",
- waitUs);
- }
+ if (status_t status = rpcSend(
+ connection, session, "transaction", iovs, arraysize(iovs),
+ [&] {
+ if (waitUs > kWaitLogUs) {
+ ALOGE("Cannot send command, trying to process pending refcounts. Waiting "
+ "%zuus. Too many oneway calls?",
+ waitUs);
+ }
- if (waitUs > 0) {
- usleep(waitUs);
- waitUs = std::min(kWaitMaxUs, waitUs * 2);
- } else {
- waitUs = 1;
- }
+ if (waitUs > 0) {
+ usleep(waitUs);
+ waitUs = std::min(kWaitMaxUs, waitUs * 2);
+ } else {
+ waitUs = 1;
+ }
- return drainCommands(connection, session,
- CommandType::CONTROL_ONLY);
- });
+ return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+ },
+ rpcFields->mFds.get());
status != OK) {
// TODO(b/167966510): need to undo onBinderLeaving - we know the
// refcount isn't successfully transferred.
@@ -581,18 +592,26 @@
status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply) {
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
RpcWireHeader command;
while (true) {
iovec iov{&command, sizeof(command)};
- if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1);
+ if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1,
+ enableAncillaryFds(session->getFileDescriptorTransportMode())
+ ? &ancillaryFds
+ : nullptr);
status != OK)
return status;
if (command.command == RPC_COMMAND_REPLY) break;
- if (status_t status = processCommand(connection, session, command, CommandType::ANY);
+ if (status_t status = processCommand(connection, session, command, CommandType::ANY,
+ std::move(ancillaryFds));
status != OK)
return status;
+
+ // Reset to avoid spurious use-after-move warning from clang-tidy.
+ ancillaryFds = decltype(ancillaryFds)();
}
const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value());
@@ -614,21 +633,33 @@
{&rpcReply, rpcReplyWireSize},
{data.data(), data.size()},
};
- if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs));
+ if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr);
status != OK)
return status;
+
if (rpcReply.status != OK) return rpcReply.status;
Span<const uint8_t> parcelSpan = {data.data(), data.size()};
+ Span<const uint32_t> objectTableSpan;
if (session->getProtocolVersion().value() >=
RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(rpcReply.parcelDataSize);
- LOG_ALWAYS_FATAL_IF(objectTableBytes.size > 0, "Non-empty object table not supported yet.");
+ std::optional<Span<const uint32_t>> maybeSpan =
+ objectTableBytes.reinterpret<const uint32_t>();
+ if (!maybeSpan.has_value()) {
+ ALOGE("Bad object table size inferred from RpcWireReply. Saw bodySize=%" PRId32
+ " sizeofHeader=%zu parcelSize=%" PRId32 " objectTableBytesSize=%zu. Terminating!",
+ command.bodySize, rpcReplyWireSize, rpcReply.parcelDataSize,
+ objectTableBytes.size);
+ return BAD_VALUE;
+ }
+ objectTableSpan = *maybeSpan;
}
data.release();
- reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, cleanup_reply_data);
- return OK;
+ return reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size,
+ objectTableSpan.data, objectTableSpan.size,
+ std::move(ancillaryFds), cleanup_reply_data);
}
status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection,
@@ -639,7 +670,7 @@
};
{
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(addr);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -671,13 +702,17 @@
const sp<RpcSession>& session, CommandType type) {
LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
RpcWireHeader command;
iovec iov{&command, sizeof(command)};
- if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1);
+ if (status_t status =
+ rpcRec(connection, session, "command header (for server)", &iov, 1,
+ enableAncillaryFds(session->getFileDescriptorTransportMode()) ? &ancillaryFds
+ : nullptr);
status != OK)
return status;
- return processCommand(connection, session, command, type);
+ return processCommand(connection, session, command, type, std::move(ancillaryFds));
}
status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection,
@@ -693,28 +728,33 @@
return OK;
}
-status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcWireHeader& command,
- CommandType type) {
+status_t RpcState::processCommand(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command, CommandType type,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
+#ifdef BINDER_WITH_KERNEL_IPC
IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
IPCThreadState::SpGuard spGuard{
.address = __builtin_frame_address(0),
- .context = "processing binder RPC command",
+ .context = "processing binder RPC command (where RpcServer::setPerSessionRootObject is "
+ "used to distinguish callers)",
};
const IPCThreadState::SpGuard* origGuard;
if (kernelBinderState != nullptr) {
origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
}
- ScopeGuard guardUnguard = [&]() {
+
+ base::ScopeGuard guardUnguard = [&]() {
if (kernelBinderState != nullptr) {
kernelBinderState->restoreGetCallingSpGuard(origGuard);
}
};
+#endif // BINDER_WITH_KERNEL_IPC
switch (command.command) {
case RPC_COMMAND_TRANSACT:
if (type != CommandType::ANY) return BAD_TYPE;
- return processTransact(connection, session, command);
+ return processTransact(connection, session, command, std::move(ancillaryFds));
case RPC_COMMAND_DEC_STRONG:
return processDecStrong(connection, session, command);
}
@@ -728,8 +768,10 @@
(void)session->shutdownAndWait(false);
return DEAD_OBJECT;
}
-status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcWireHeader& command) {
+status_t RpcState::processTransact(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
CommandData transactionData(command.bodySize);
@@ -737,10 +779,12 @@
return NO_MEMORY;
}
iovec iov{transactionData.data(), transactionData.size()};
- if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1); status != OK)
+ if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1, nullptr);
+ status != OK)
return status;
- return processTransactInternal(connection, session, std::move(transactionData));
+ return processTransactInternal(connection, session, std::move(transactionData),
+ std::move(ancillaryFds));
}
static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -752,9 +796,10 @@
(void)objectsCount;
}
-status_t RpcState::processTransactInternal(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- CommandData transactionData) {
+status_t RpcState::processTransactInternal(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ CommandData transactionData,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
// for 'recursive' calls to this, we have already read and processed the
// binder from the transaction data and taken reference counts into account,
// so it is cached here.
@@ -798,7 +843,7 @@
(void)session->shutdownAndWait(false);
replyStatus = BAD_VALUE;
} else if (oneway) {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it->second.binder.promote() != target) {
ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr);
@@ -845,11 +890,21 @@
Span<const uint8_t> parcelSpan = {transaction->data,
transactionData.size() -
offsetof(RpcWireTransaction, data)};
- if (session->getProtocolVersion().value() >=
+ Span<const uint32_t> objectTableSpan;
+ if (session->getProtocolVersion().value() >
RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(transaction->parcelDataSize);
- LOG_ALWAYS_FATAL_IF(objectTableBytes.size > 0,
- "Non-empty object table not supported yet.");
+ std::optional<Span<const uint32_t>> maybeSpan =
+ objectTableBytes.reinterpret<const uint32_t>();
+ if (!maybeSpan.has_value()) {
+ ALOGE("Bad object table size inferred from RpcWireTransaction. Saw bodySize=%zu "
+ "sizeofHeader=%zu parcelSize=%" PRId32
+ " objectTableBytesSize=%zu. Terminating!",
+ transactionData.size(), sizeof(RpcWireTransaction),
+ transaction->parcelDataSize, objectTableBytes.size);
+ return BAD_VALUE;
+ }
+ objectTableSpan = *maybeSpan;
}
Parcel data;
@@ -857,47 +912,53 @@
// only holds onto it for the duration of this function call. Parcel will be
// deleted before the 'transactionData' object.
- data.rpcSetDataReference(session, parcelSpan.data, parcelSpan.size,
- do_nothing_to_transact_data);
+ replyStatus =
+ data.rpcSetDataReference(session, parcelSpan.data, parcelSpan.size,
+ objectTableSpan.data, objectTableSpan.size,
+ std::move(ancillaryFds), do_nothing_to_transact_data);
+ // Reset to avoid spurious use-after-move warning from clang-tidy.
+ ancillaryFds = std::remove_reference<decltype(ancillaryFds)>::type();
- if (target) {
- bool origAllowNested = connection->allowNested;
- connection->allowNested = !oneway;
+ if (replyStatus == OK) {
+ if (target) {
+ bool origAllowNested = connection->allowNested;
+ connection->allowNested = !oneway;
- replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
+ replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
- connection->allowNested = origAllowNested;
- } else {
- LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
+ connection->allowNested = origAllowNested;
+ } else {
+ LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
- switch (transaction->code) {
- case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
- replyStatus = reply.writeInt32(session->getMaxIncomingThreads());
- break;
- }
- case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
- // for client connections, this should always report the value
- // originally returned from the server, so this is asserting
- // that it exists
- replyStatus = reply.writeByteVector(session->mId);
- break;
- }
- default: {
- sp<RpcServer> server = session->server();
- if (server) {
- switch (transaction->code) {
- case RPC_SPECIAL_TRANSACT_GET_ROOT: {
- sp<IBinder> root = session->mSessionSpecificRootObject
- ?: server->getRootObject();
- replyStatus = reply.writeStrongBinder(root);
- break;
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
+ replyStatus = reply.writeInt32(session->getMaxIncomingThreads());
+ break;
+ }
+ case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
+ // for client connections, this should always report the value
+ // originally returned from the server, so this is asserting
+ // that it exists
+ replyStatus = reply.writeByteVector(session->mId);
+ break;
+ }
+ default: {
+ sp<RpcServer> server = session->server();
+ if (server) {
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+ sp<IBinder> root = session->mSessionSpecificRootObject
+ ?: server->getRootObject();
+ replyStatus = reply.writeStrongBinder(root);
+ break;
+ }
+ default: {
+ replyStatus = UNKNOWN_TRANSACTION;
+ }
}
- default: {
- replyStatus = UNKNOWN_TRANSACTION;
- }
+ } else {
+ ALOGE("Special command sent, but no server object attached.");
}
- } else {
- ALOGE("Special command sent, but no server object attached.");
}
}
}
@@ -923,7 +984,7 @@
// downside: asynchronous transactions may drown out synchronous
// transactions.
{
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
// last refcount dropped after this transaction happened
if (it == mNodeForAddress.end()) return OK;
@@ -969,11 +1030,22 @@
replyStatus = flushExcessBinderRefs(session, addr, target);
}
+ std::string errorMsg;
+ if (status_t status = validateParcel(session, reply, &errorMsg); status != OK) {
+ ALOGE("Reply Parcel failed validation: %s", errorMsg.c_str());
+ // Forward the error to the client of the transaction.
+ reply.freeData();
+ reply.markForRpc(session);
+ replyStatus = status;
+ }
+
+ auto* rpcFields = reply.maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+
const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value());
- // objectTable always empty for now. Will be populated from `reply` soon.
- std::vector<uint32_t> objectTable;
- Span<const uint32_t> objectTableSpan = {objectTable.data(), objectTable.size()};
+ Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(),
+ rpcFields->mObjectPositions.size()};
uint32_t bodySize;
LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(rpcReplyWireSize, reply.dataSize(), &bodySize) ||
@@ -998,7 +1070,8 @@
{const_cast<uint8_t*>(reply.data()), reply.dataSize()},
objectTableSpan.toIovec(),
};
- return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt);
+ return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt,
+ rpcFields->mFds.get());
}
status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection,
@@ -1014,11 +1087,12 @@
RpcDecStrong body;
iovec iov{&body, sizeof(RpcDecStrong)};
- if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK)
+ if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1, nullptr);
+ status != OK)
return status;
uint64_t addr = RpcWireAddress::toRaw(body.address);
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr);
@@ -1055,6 +1129,50 @@
return OK;
}
+status_t RpcState::validateParcel(const sp<RpcSession>& session, const Parcel& parcel,
+ std::string* errorMsg) {
+ auto* rpcFields = parcel.maybeRpcFields();
+ if (rpcFields == nullptr) {
+ *errorMsg = "Parcel not crafted for RPC call";
+ return BAD_TYPE;
+ }
+
+ if (rpcFields->mSession != session) {
+ *errorMsg = "Parcel's session doesn't match";
+ return BAD_TYPE;
+ }
+
+ uint32_t protocolVersion = session->getProtocolVersion().value();
+ if (protocolVersion < RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE &&
+ !rpcFields->mObjectPositions.empty()) {
+ *errorMsg = StringPrintf("Parcel has attached objects but the session's protocol version "
+ "(%" PRIu32 ") is too old, must be at least %" PRIu32,
+ protocolVersion,
+ RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE);
+ return BAD_VALUE;
+ }
+
+ if (rpcFields->mFds && !rpcFields->mFds->empty()) {
+ switch (session->getFileDescriptorTransportMode()) {
+ case RpcSession::FileDescriptorTransportMode::NONE:
+ *errorMsg =
+ "Parcel has file descriptors, but no file descriptor transport is enabled";
+ return FDS_NOT_ALLOWED;
+ case RpcSession::FileDescriptorTransportMode::UNIX: {
+ constexpr size_t kMaxFdsPerMsg = 253;
+ if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
+ *errorMsg = StringPrintf("Too many file descriptors in Parcel for unix "
+ "domain socket: %zu (max is %zu)",
+ rpcFields->mFds->size(), kMaxFdsPerMsg);
+ return BAD_VALUE;
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) {
sp<IBinder> ref;
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 9cbe187..6fb2e4a 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -19,6 +19,7 @@
#include <binder/IBinder.h>
#include <binder/Parcel.h>
#include <binder/RpcSession.h>
+#include <binder/RpcThreads.h>
#include <map>
#include <optional>
@@ -181,26 +182,36 @@
[[nodiscard]] status_t rpcSend(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
const char* what, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll);
- [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs,
- int niovs);
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds =
+ nullptr);
+ [[nodiscard]] status_t rpcRec(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr);
[[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply);
- [[nodiscard]] status_t processCommand(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- const RpcWireHeader& command, CommandType type);
- [[nodiscard]] status_t processTransact(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- const RpcWireHeader& command);
- [[nodiscard]] status_t processTransactInternal(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- CommandData transactionData);
+ [[nodiscard]] status_t processCommand(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command, CommandType type,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ [[nodiscard]] status_t processTransact(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ [[nodiscard]] status_t processTransactInternal(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ CommandData transactionData,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
[[nodiscard]] status_t processDecStrong(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session,
const RpcWireHeader& command);
+ // Whether `parcel` is compatible with `session`.
+ [[nodiscard]] static status_t validateParcel(const sp<RpcSession>& session,
+ const Parcel& parcel, std::string* errorMsg);
+
struct BinderNode {
// Two cases:
// A - local binder we are serving
@@ -258,7 +269,7 @@
// false - session shutdown, halt
[[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node);
- std::mutex mNodeMutex;
+ RpcMutex mNodeMutex;
bool mTerminated = false;
uint32_t mNextId = 0;
// binders known by both sides of a session
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index f9b73fc..7cc58cd 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -18,6 +18,7 @@
#include <log/log.h>
#include <poll.h>
+#include <stddef.h>
#include <binder/RpcTransportRaw.h>
@@ -28,6 +29,9 @@
namespace {
+// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
+constexpr size_t kMaxFdsPerMsg = 253;
+
// RpcTransport with TLS disabled.
class RpcTransportRaw : public RpcTransport {
public:
@@ -85,15 +89,7 @@
bool havePolled = false;
while (true) {
- msghdr msg{
- .msg_iov = iovs,
- // posix uses int, glibc uses size_t. niovs is a
- // non-negative int and can be cast to either.
- .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
- };
- ssize_t processSize =
- TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL));
-
+ ssize_t processSize = sendOrReceiveFun(iovs, niovs);
if (processSize < 0) {
int savedErrno = errno;
@@ -145,16 +141,121 @@
status_t interruptableWriteFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) override {
- return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT,
- altPoll);
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+ override {
+ bool sentFds = false;
+ auto send = [&](iovec* iovs, int niovs) -> ssize_t {
+ if (ancillaryFds != nullptr && !ancillaryFds->empty() && !sentFds) {
+ if (ancillaryFds->size() > kMaxFdsPerMsg) {
+ // This shouldn't happen because we check the FD count in RpcState.
+ ALOGE("Saw too many file descriptors in RpcTransportCtxRaw: %zu (max is %zu). "
+ "Aborting session.",
+ ancillaryFds->size(), kMaxFdsPerMsg);
+ errno = EINVAL;
+ return -1;
+ }
+
+ // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
+ // use memcpy.
+ int fds[kMaxFdsPerMsg];
+ for (size_t i = 0; i < ancillaryFds->size(); i++) {
+ fds[i] = std::visit([](const auto& fd) { return fd.get(); },
+ ancillaryFds->at(i));
+ }
+ const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
+
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
+ memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
+
+ msg.msg_controllen = CMSG_SPACE(fdsByteSize);
+
+ ssize_t processedSize = TEMP_FAILURE_RETRY(
+ sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
+ if (processedSize > 0) {
+ sentFds = true;
+ }
+ return processedSize;
+ }
+
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+ return TEMP_FAILURE_RETRY(sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+ };
+ return interruptableReadOrWrite(fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT, altPoll);
}
status_t interruptableReadFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) override {
- return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN,
- altPoll);
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
+ auto recv = [&](iovec* iovs, int niovs) -> ssize_t {
+ if (ancillaryFds != nullptr) {
+ int fdBuffer[kMaxFdsPerMsg];
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+ ssize_t processSize =
+ TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+ if (processSize < 0) {
+ return -1;
+ }
+
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
+ // application devs to memcpy the data to ensure memory alignment.
+ size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
+ LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // sanity check
+ memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
+ size_t fdCount = dataLen / sizeof(int);
+ ancillaryFds->reserve(ancillaryFds->size() + fdCount);
+ for (size_t i = 0; i < fdCount; i++) {
+ ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
+ }
+ break;
+ }
+ }
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ ALOGE("msg was truncated. Aborting session.");
+ errno = EPIPE;
+ return -1;
+ }
+
+ return processSize;
+ }
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+ return TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+ };
+ return interruptableReadOrWrite(fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN, altPoll);
}
private:
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index ad5cb0f..09b5c17 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -282,10 +282,13 @@
status_t pollRead(void) override;
status_t interruptableWriteFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) override;
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+ override;
status_t interruptableReadFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) override;
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
private:
android::base::unique_fd mSocket;
@@ -313,7 +316,10 @@
status_t RpcTransportTls::interruptableWriteFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ (void)ancillaryFds;
+
MAYBE_WAIT_IN_FLAKE_MODE;
if (niovs < 0) return BAD_VALUE;
@@ -356,7 +362,10 @@
status_t RpcTransportTls::interruptableReadFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ (void)ancillaryFds;
+
MAYBE_WAIT_IN_FLAKE_MODE;
if (niovs < 0) return BAD_VALUE;
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 7e2aa79..ff1b01a 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -45,7 +45,8 @@
struct RpcConnectionHeader {
uint32_t version; // maximum supported by caller
uint8_t options;
- uint8_t reservered[9];
+ uint8_t fileDescriptorTransportMode;
+ uint8_t reservered[8];
// Follows is sessionIdSize bytes.
// if size is 0, this is requesting a new session.
uint16_t sessionIdSize;
@@ -108,6 +109,10 @@
// serialization is like:
// |RpcWireHeader|struct desginated by 'command'| (over and over again)
+//
+// When file descriptors are included in out-of-band data (e.g. in unix domain
+// sockets), they are always paired with the RpcWireHeader bytes of the
+// transaction or reply the file descriptors belong to.
struct RpcWireHeader {
uint32_t command; // RPC_COMMAND_*
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 0232f50..c91d56c 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -28,9 +28,6 @@
"name": "binderLibTest"
},
{
- "name": "binderRpcTest"
- },
- {
"name": "binderStabilityTest"
},
{
@@ -84,7 +81,21 @@
"name": "rustBinderSerializationTest"
}
],
- "hwasan-presubmit": [
+ "presubmit-large": [
+ {
+ "name": "binderRpcTest"
+ },
+ {
+ "name": "binderRpcTestNoKernel"
+ },
+ {
+ "name": "binderRpcTestSingleThreaded"
+ },
+ {
+ "name": "binderRpcTestSingleThreadedNoKernel"
+ }
+ ],
+ "hwasan-presubmit": [
{
"name": "binderLibTest"
}
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
index b0289a7..0314b0f 100644
--- a/libs/binder/Utils.cpp
+++ b/libs/binder/Utils.cpp
@@ -16,40 +16,12 @@
#include "Utils.h"
-#include <android-base/file.h>
#include <string.h>
-using android::base::ErrnoError;
-using android::base::Result;
-
namespace android {
void zeroMemory(uint8_t* data, size_t size) {
memset(data, 0, size);
}
-Result<void> setNonBlocking(android::base::borrowed_fd fd) {
- int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
- if (flags == -1) {
- return ErrnoError() << "Could not get flags for fd";
- }
- if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) {
- return ErrnoError() << "Could not set non-blocking flag for fd";
- }
- return {};
-}
-
-status_t getRandomBytes(uint8_t* data, size_t size) {
- int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
- if (ret == -1) {
- return -errno;
- }
-
- base::unique_fd fd(ret);
- if (!base::ReadFully(fd, data, size)) {
- return -errno;
- }
- return OK;
-}
-
} // namespace android
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index 7dcb70e..7c6d6f1 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -15,11 +15,10 @@
*/
#include <stddef.h>
+#include <sys/uio.h>
#include <cstdint>
#include <optional>
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
#include <log/log.h>
#include <utils/Errors.h>
@@ -36,10 +35,6 @@
// avoid optimizations
void zeroMemory(uint8_t* data, size_t size);
-android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
-
-status_t getRandomBytes(uint8_t* data, size_t size);
-
// View of contiguous sequence. Similar to std::span.
template <typename T>
struct Span {
@@ -60,6 +55,17 @@
size = offset;
return rest;
}
+
+ // Returns nullopt if the byte size of `this` isn't evenly divisible by sizeof(U).
+ template <typename U>
+ std::optional<Span<U>> reinterpret() const {
+ // Only allow casting from bytes for simplicity.
+ static_assert(std::is_same_v<std::remove_const_t<T>, uint8_t>);
+ if (size % sizeof(U) != 0) {
+ return std::nullopt;
+ }
+ return Span<U>{reinterpret_cast<U*>(data), size / sizeof(U)};
+ }
};
} // namespace android
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 1576c94..dc572ac 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -72,9 +72,9 @@
public:
virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
virtual const String16& getInterfaceDescriptor() const;
+ typedef INTERFACE BaseInterface;
protected:
- typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
@@ -85,9 +85,9 @@
{
public:
explicit BpInterface(const sp<IBinder>& remote);
+ typedef INTERFACE BaseInterface;
protected:
- typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index cd6a274..8ce3bc9 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -28,6 +28,10 @@
// ---------------------------------------------------------------------------
namespace android {
+/**
+ * Kernel binder thread state. All operations here refer to kernel binder. This
+ * object is allocated per-thread.
+ */
class IPCThreadState
{
public:
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 0345a5d..91febbd 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -608,8 +608,12 @@
size_t ipcObjectsCount() const;
void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
size_t objectsCount, release_func relFunc);
- void rpcSetDataReference(const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
- release_func relFunc);
+ // Takes ownership even when an error is returned.
+ status_t rpcSetDataReference(
+ const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
+ const uint32_t* objectTable, size_t objectTableSize,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds,
+ release_func relFunc);
status_t finishWrite(size_t len);
void releaseObjects();
@@ -620,6 +624,7 @@
status_t restartWrite(size_t desired);
// Set the capacity to `desired`, truncating the Parcel if necessary.
status_t continueWrite(size_t desired);
+ status_t truncateRpcObjects(size_t newObjectsSize);
status_t writePointer(uintptr_t val);
status_t readPointer(uintptr_t *pArg) const;
uintptr_t readPointer() const;
@@ -1179,10 +1184,20 @@
c->clear(); // must clear before resizing/reserving otherwise move ctors may be called.
if constexpr (is_pointer_equivalent_array_v<T>) {
// could consider POD without gaps and alignment of 4.
- auto data = reinterpret_cast<const T*>(
- readInplace(static_cast<size_t>(size) * sizeof(T)));
+ size_t dataLen;
+ if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) {
+ return -EOVERFLOW;
+ }
+ auto data = reinterpret_cast<const T*>(readInplace(dataLen));
if (data == nullptr) return BAD_VALUE;
- c->insert(c->begin(), data, data + size); // insert should do a reserve().
+ // std::vector::insert and similar methods will require type-dependent
+ // byte alignment when inserting from a const iterator such as `data`,
+ // e.g. 8 byte alignment for int64_t, and so will not work if `data`
+ // is 4 byte aligned (which is all Parcel guarantees). Copying
+ // the contents into the vector directly, where possible, circumvents
+ // this.
+ c->resize(size);
+ memcpy(c->data(), data, dataLen);
} else if constexpr (std::is_same_v<T, bool>
|| std::is_same_v<T, char16_t>) {
c->reserve(size); // avoids default initialization
@@ -1279,6 +1294,23 @@
// Should always be non-null.
const sp<RpcSession> mSession;
+
+ enum ObjectType : int32_t {
+ TYPE_BINDER_NULL = 0,
+ TYPE_BINDER = 1,
+ // FD to be passed via native transport (Trusty IPC or UNIX domain socket).
+ TYPE_NATIVE_FILE_DESCRIPTOR = 2,
+ };
+
+ // Sorted.
+ std::vector<uint32_t> mObjectPositions;
+
+ // File descriptors referenced by the parcel data. Should be indexed
+ // using the offsets in the parcel data. Don't assume the list is in the
+ // same order as `mObjectPositions`.
+ //
+ // Boxed to save space. Lazy allocated.
+ std::unique_ptr<std::vector<std::variant<base::unique_fd, base::borrowed_fd>>> mFds;
};
std::variant<KernelFields, RpcFields> mVariantFields;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index e17a76c..882dfbf 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -29,6 +29,10 @@
class IPCThreadState;
+/**
+ * Kernel binder process state. All operations here refer to kernel binder. This
+ * object is allocated per process.
+ */
class ProcessState : public virtual RefBase {
public:
static sp<ProcessState> self();
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index dba8dd6..9318c27 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -18,6 +18,7 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/RpcSession.h>
+#include <binder/RpcThreads.h>
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -114,6 +115,15 @@
void setProtocolVersion(uint32_t version);
/**
+ * Set the supported transports for sending and receiving file descriptors.
+ *
+ * Clients will propose a mode when connecting. If the mode is not in the
+ * provided list, the connection will be rejected.
+ */
+ void setSupportedFileDescriptorTransportModes(
+ const std::vector<RpcSession::FileDescriptorTransportMode>& modes);
+
+ /**
* The root object can be retrieved by any client, without any
* authentication. TODO(b/183988761)
*
@@ -193,18 +203,22 @@
const std::unique_ptr<RpcTransportCtx> mCtx;
size_t mMaxThreads = 1;
std::optional<uint32_t> mProtocolVersion;
+ // A mode is supported if the N'th bit is on, where N is the mode enum's value.
+ std::bitset<8> mSupportedFileDescriptorTransportModes = std::bitset<8>().set(
+ static_cast<size_t>(RpcSession::FileDescriptorTransportMode::NONE));
base::unique_fd mServer; // socket we are accepting sessions on
- std::mutex mLock; // for below
- std::unique_ptr<std::thread> mJoinThread;
+ RpcMutex mLock; // for below
+ std::unique_ptr<RpcMaybeThread> mJoinThread;
bool mJoinThreadRunning = false;
- std::map<std::thread::id, std::thread> mConnectingThreads;
+ std::map<RpcMaybeThread::id, RpcMaybeThread> mConnectingThreads;
+
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
- std::condition_variable mShutdownCv;
+ RpcConditionVariable mShutdownCv;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index e76b140..a2b28db 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -18,13 +18,13 @@
#include <android-base/threads.h>
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
+#include <binder/RpcThreads.h>
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <map>
#include <optional>
-#include <thread>
#include <vector>
namespace android {
@@ -95,6 +95,18 @@
[[nodiscard]] bool setProtocolVersion(uint32_t version);
std::optional<uint32_t> getProtocolVersion();
+ enum class FileDescriptorTransportMode : uint8_t {
+ NONE = 0,
+ // Send file descriptors via unix domain socket ancillary data.
+ UNIX = 1,
+ };
+
+ /**
+ * Set the transport for sending and receiving file descriptors.
+ */
+ void setFileDescriptorTransportMode(FileDescriptorTransportMode mode);
+ FileDescriptorTransportMode getFileDescriptorTransportMode();
+
/**
* This should be called once per thread, matching 'join' in the remote
* process.
@@ -206,10 +218,10 @@
public:
void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
- void waitForShutdown(std::unique_lock<std::mutex>& lock, const sp<RpcSession>& session);
+ void waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session);
private:
- std::condition_variable mCv;
+ RpcConditionVariable mCv;
};
friend WaitForShutdownListener;
@@ -232,7 +244,7 @@
//
// transfer ownership of thread (usually done while a lock is taken on the
// structure which originally owns the thread)
- void preJoinThreadOwnership(std::thread thread);
+ void preJoinThreadOwnership(RpcMaybeThread thread);
// pass FD to thread and read initial connection information
struct PreJoinSetupResult {
// Server connection object associated with this
@@ -264,6 +276,7 @@
sp<RpcConnection> assignIncomingConnectionToThisThread(
std::unique_ptr<RpcTransport> rpcTransport);
[[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
+ void clearConnectionTid(const sp<RpcConnection>& connection);
[[nodiscard]] status_t initShutdownTrigger();
@@ -313,7 +326,7 @@
// For a more complicated case, the client might itself open up a thread to
// serve calls to the server at all times (e.g. if it hosts a callback)
- wp<RpcServer> mForServer; // maybe null, for client sessions
+ wp<RpcServer> mForServer; // maybe null, for client sessions
sp<WaitForShutdownListener> mShutdownListener; // used for client sessions
wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client
@@ -327,13 +340,14 @@
std::unique_ptr<RpcState> mRpcBinderState;
- std::mutex mMutex; // for all below
+ RpcMutex mMutex; // for all below
size_t mMaxIncomingThreads = 0;
size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
std::optional<uint32_t> mProtocolVersion;
+ FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE;
- std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
+ RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads
struct ThreadState {
size_t mWaitingThreads = 0;
@@ -342,7 +356,7 @@
std::vector<sp<RpcConnection>> mOutgoing;
size_t mMaxIncoming = 0;
std::vector<sp<RpcConnection>> mIncoming;
- std::map<std::thread::id, std::thread> mThreads;
+ std::map<RpcMaybeThread::id, RpcMaybeThread> mThreads;
} mConnections;
};
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
new file mode 100644
index 0000000..8abf04e
--- /dev/null
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <pthread.h>
+
+#include <android-base/threads.h>
+
+#include <functional>
+#include <memory>
+#include <thread>
+
+namespace android {
+
+#ifdef BINDER_RPC_SINGLE_THREADED
+class RpcMutex {
+public:
+ void lock() {}
+ void unlock() {}
+};
+
+class RpcMutexUniqueLock {
+public:
+ RpcMutexUniqueLock(RpcMutex&) {}
+ void unlock() {}
+};
+
+class RpcMutexLockGuard {
+public:
+ RpcMutexLockGuard(RpcMutex&) {}
+};
+
+class RpcConditionVariable {
+public:
+ void notify_one() {}
+ void notify_all() {}
+
+ void wait(RpcMutexUniqueLock&) {}
+
+ template <typename Predicate>
+ void wait(RpcMutexUniqueLock&, Predicate stop_waiting) {
+ LOG_ALWAYS_FATAL_IF(!stop_waiting(), "RpcConditionVariable::wait condition not met");
+ }
+
+ template <typename Duration>
+ std::cv_status wait_for(RpcMutexUniqueLock&, const Duration&) {
+ return std::cv_status::no_timeout;
+ }
+
+ template <typename Duration, typename Predicate>
+ bool wait_for(RpcMutexUniqueLock&, const Duration&, Predicate stop_waiting) {
+ return stop_waiting();
+ }
+};
+
+class RpcMaybeThread {
+public:
+ RpcMaybeThread() = default;
+
+ template <typename Function, typename... Args>
+ RpcMaybeThread(Function&& f, Args&&... args) {
+ // std::function requires a copy-constructible closure,
+ // so we need to wrap both the function and its arguments
+ // in a shared pointer that std::function can copy internally
+ struct Vars {
+ std::decay_t<Function> f;
+ std::tuple<std::decay_t<Args>...> args;
+
+ explicit Vars(Function&& f, Args&&... args)
+ : f(std::move(f)), args(std::move(args)...) {}
+ };
+ auto vars = std::make_shared<Vars>(std::forward<Function>(f), std::forward<Args>(args)...);
+ mFunc = [vars]() { std::apply(std::move(vars->f), std::move(vars->args)); };
+ }
+
+ void join() {
+ if (mFunc) {
+ // Move mFunc into a temporary so we can clear mFunc before
+ // executing the callback. This avoids infinite recursion if
+ // the callee then calls join() again directly or indirectly.
+ decltype(mFunc) func = nullptr;
+ mFunc.swap(func);
+ func();
+ }
+ }
+ void detach() { join(); }
+
+ class id {
+ public:
+ bool operator==(const id&) const { return true; }
+ bool operator!=(const id&) const { return false; }
+ bool operator<(const id&) const { return false; }
+ bool operator<=(const id&) const { return true; }
+ bool operator>(const id&) const { return false; }
+ bool operator>=(const id&) const { return true; }
+ };
+
+ id get_id() const { return id(); }
+
+private:
+ std::function<void(void)> mFunc;
+};
+
+namespace rpc_this_thread {
+static inline RpcMaybeThread::id get_id() {
+ return RpcMaybeThread::id();
+}
+} // namespace rpc_this_thread
+
+static inline uint64_t rpcGetThreadId() {
+ return 0;
+}
+
+static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) {
+ t.join();
+}
+#else // BINDER_RPC_SINGLE_THREADED
+using RpcMutex = std::mutex;
+using RpcMutexUniqueLock = std::unique_lock<std::mutex>;
+using RpcMutexLockGuard = std::lock_guard<std::mutex>;
+using RpcConditionVariable = std::condition_variable;
+using RpcMaybeThread = std::thread;
+namespace rpc_this_thread = std::this_thread;
+
+static inline uint64_t rpcGetThreadId() {
+ return base::GetThreadId();
+}
+
+static inline void rpcJoinIfSingleThreaded(RpcMaybeThread&) {}
+#endif // BINDER_RPC_SINGLE_THREADED
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index ee4b548..5197ef9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -22,6 +22,8 @@
#include <memory>
#include <optional>
#include <string>
+#include <variant>
+#include <vector>
#include <android-base/function_ref.h>
#include <android-base/unique_fd.h>
@@ -61,16 +63,23 @@
* to read/write data. If this returns an error, that error is returned from
* this function.
*
+ * ancillaryFds - FDs to be sent via UNIX domain dockets or Trusty IPC. When
+ * reading, if `ancillaryFds` is null, any received FDs will be silently
+ * dropped and closed (by the OS). Appended values will always be unique_fd,
+ * the variant type is used to avoid extra copies elsewhere.
+ *
* Return:
* OK - succeeded in completely processing 'size'
* error - interrupted (failure or trigger)
*/
[[nodiscard]] virtual status_t interruptableWriteFully(
FdTrigger *fdTrigger, iovec *iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>> &altPoll) = 0;
+ const std::optional<android::base::function_ref<status_t()>> &altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
[[nodiscard]] virtual status_t interruptableReadFully(
FdTrigger *fdTrigger, iovec *iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>> &altPoll) = 0;
+ const std::optional<android::base::function_ref<status_t()>> &altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
protected:
RpcTransport() = default;
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 3824a1b..f3f2886 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -146,8 +146,8 @@
AIBinder_Class_disableInterfaceTokenHeader;
AIBinder_DeathRecipient_setOnUnlinked;
AIBinder_isHandlingTransaction;
- AIBinder_setInheritRt; # llndk
- AIBinder_setMinSchedulerPolicy; # llndk
+ AIBinder_setInheritRt; # apex llndk
+ AIBinder_setMinSchedulerPolicy; # apex llndk
AParcel_marshal;
AParcel_unmarshal;
};
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 2f96d0e..d7c6d49 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -141,6 +141,7 @@
unstable: true,
srcs: [
"BinderRpcTestClientInfo.aidl",
+ "BinderRpcTestServerConfig.aidl",
"BinderRpcTestServerInfo.aidl",
"IBinderRpcCallback.aidl",
"IBinderRpcSession.aidl",
@@ -167,7 +168,6 @@
"libbinder_tls_shared_deps",
],
shared_libs: [
- "libbinder",
"libbase",
"liblog",
],
@@ -185,25 +185,72 @@
],
}
-cc_test {
- name: "binderRpcTest",
+cc_defaults {
+ name: "binderRpcTest_common_defaults",
host_supported: true,
target: {
darwin: {
enabled: false,
},
+ },
+ defaults: [
+ "binder_test_defaults",
+ ],
+
+ static_libs: [
+ "libbinder_tls_static",
+ "libbinder_tls_test_utils",
+ "binderRpcTestIface-cpp",
+ "binderRpcTestIface-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "binderRpcTest_service_defaults",
+ defaults: [
+ "binderRpcTest_common_defaults",
+ ],
+ gtest: false,
+ auto_gen_config: false,
+ srcs: [
+ "binderRpcTestCommon.cpp",
+ "binderRpcTestService.cpp",
+ ],
+}
+
+cc_defaults {
+ name: "binderRpcTest_defaults",
+ target: {
android: {
test_suites: ["vts"],
},
},
defaults: [
- "binder_test_defaults",
- "libbinder_tls_shared_deps",
+ "binderRpcTest_common_defaults",
],
srcs: [
"binderRpcTest.cpp",
+ "binderRpcTestCommon.cpp",
],
+
+ test_suites: ["general-tests"],
+ require_root: true,
+
+ data_bins: [
+ "binder_rpc_test_service",
+ "binder_rpc_test_service_no_kernel",
+ "binder_rpc_test_service_single_threaded",
+ "binder_rpc_test_service_single_threaded_no_kernel",
+ ],
+}
+
+cc_defaults {
+ name: "binderRpcTest_shared_defaults",
+ cflags: [
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+
shared_libs: [
"libbinder",
"libbinder_ndk",
@@ -212,14 +259,128 @@
"libcutils",
"liblog",
],
- static_libs: [
- "libbinder_tls_static",
- "libbinder_tls_test_utils",
- "binderRpcTestIface-cpp",
- "binderRpcTestIface-ndk",
+}
+
+cc_defaults {
+ name: "binderRpcTest_static_defaults",
+
+ shared_libs: [
+ "libutils",
+ // libcrypto_static is not visible to this module
+ "libcrypto",
],
- test_suites: ["general-tests"],
- require_root: true,
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libssl",
+ ],
+
+ cflags: [
+ // Disable tests that require shared libraries,
+ // e.g., libbinder.so or libbinder_ndk.so
+ "-DBINDER_TEST_NO_SHARED_LIBS",
+ ],
+}
+
+cc_test {
+ // The module name cannot start with "binderRpcTest" because
+ // then atest tries to execute it as part of binderRpcTest
+ name: "binder_rpc_test_service",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_shared_defaults",
+ "libbinder_tls_shared_deps",
+ ],
+}
+
+cc_test {
+ name: "binder_rpc_test_service_no_kernel",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ static_libs: [
+ "libbinder_rpc_no_kernel",
+ ],
+}
+
+cc_test {
+ name: "binder_rpc_test_service_single_threaded",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded",
+ ],
+}
+
+cc_test {
+ name: "binder_rpc_test_service_single_threaded_no_kernel",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded_no_kernel",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTest",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_shared_defaults",
+ "libbinder_tls_shared_deps",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTestNoKernel",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ static_libs: [
+ "libbinder_rpc_no_kernel",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTestSingleThreaded",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTestSingleThreadedNoKernel",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded_no_kernel",
+ ],
}
cc_test {
diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl
new file mode 100644
index 0000000..34d74be
--- /dev/null
+++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcelable BinderRpcTestServerConfig {
+ int numThreads;
+ int[] serverSupportedFileDescriptorTransportModes;
+ int socketType;
+ int rpcSecurity;
+ int serverVersion;
+ int vsockPort;
+ @utf8InCpp String addr;
+}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index fdd02a4..b15a225 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -24,6 +24,9 @@
// number of known RPC binders to process, RpcState::countBinders by session
int[] countBinders();
+ // Return a null binder with a non-nullable return type.
+ IBinder getNullBinder();
+
// Caller sends server, callee pings caller's server and returns error code.
int pingMe(IBinder binder);
@nullable IBinder repeatBinder(@nullable IBinder binder);
@@ -64,4 +67,8 @@
void scheduleShutdown();
void useKernelBinderCallingId();
+
+ ParcelFileDescriptor echoAsFile(@utf8InCpp String content);
+
+ ParcelFileDescriptor concatFiles(in List<ParcelFileDescriptor> files);
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 5161469..4e41d8e 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,29 +14,7 @@
* limitations under the License.
*/
-#include <BinderRpcTestClientInfo.h>
-#include <BinderRpcTestServerInfo.h>
-#include <BnBinderRpcCallback.h>
-#include <BnBinderRpcSession.h>
-#include <BnBinderRpcTest.h>
-#include <aidl/IBinderRpcTest.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android/binder_auto_utils.h>
-#include <android/binder_libbinder.h>
-#include <binder/Binder.h>
-#include <binder/BpBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <binder/RpcServer.h>
-#include <binder/RpcSession.h>
-#include <binder/RpcTlsTestUtils.h>
-#include <binder/RpcTlsUtils.h>
-#include <binder/RpcTransport.h>
-#include <binder/RpcTransportRaw.h>
-#include <binder/RpcTransportTls.h>
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <chrono>
@@ -45,14 +23,12 @@
#include <thread>
#include <type_traits>
+#include <dlfcn.h>
#include <poll.h>
#include <sys/prctl.h>
-#include <unistd.h>
+#include <sys/socket.h>
-#include "../FdTrigger.h"
-#include "../RpcSocketAddress.h" // for testing preconnected clients
-#include "../RpcState.h" // for debugging
-#include "../vm_sockets.h" // for VMADDR_*
+#include "binderRpcTestCommon.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -62,35 +38,14 @@
namespace android {
+#ifdef BINDER_TEST_NO_SHARED_LIBS
+constexpr bool kEnableSharedLibs = false;
+#else
+constexpr bool kEnableSharedLibs = true;
+#endif
+
static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
-const char* kLocalInetAddress = "127.0.0.1";
-
-enum class RpcSecurity { RAW, TLS };
-
-static inline std::vector<RpcSecurity> RpcSecurityValues() {
- return {RpcSecurity::RAW, RpcSecurity::TLS};
-}
-
-static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
- RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr,
- std::unique_ptr<RpcAuth> auth = nullptr) {
- switch (rpcSecurity) {
- case RpcSecurity::RAW:
- return RpcTransportCtxFactoryRaw::make();
- case RpcSecurity::TLS: {
- if (verifier == nullptr) {
- verifier = std::make_shared<RpcCertificateVerifierSimple>();
- }
- if (auth == nullptr) {
- auth = std::make_unique<RpcAuthSelfSigned>();
- }
- return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth));
- }
- default:
- LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
- }
-}
TEST(BinderRpcParcel, EntireParcelFormatted) {
Parcel p;
@@ -141,191 +96,15 @@
EXPECT_TRUE(stat.isOk()) << stat; \
} while (false)
-class MyBinderRpcSession : public BnBinderRpcSession {
-public:
- static std::atomic<int32_t> gNum;
-
- MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; }
- Status getName(std::string* name) override {
- *name = mName;
- return Status::ok();
+static std::string WaitStatusToString(int wstatus) {
+ if (WIFEXITED(wstatus)) {
+ return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
}
- ~MyBinderRpcSession() { gNum--; }
-
-private:
- std::string mName;
-};
-std::atomic<int32_t> MyBinderRpcSession::gNum;
-
-class MyBinderRpcCallback : public BnBinderRpcCallback {
- Status sendCallback(const std::string& value) {
- std::unique_lock _l(mMutex);
- mValues.push_back(value);
- _l.unlock();
- mCv.notify_one();
- return Status::ok();
+ if (WIFSIGNALED(wstatus)) {
+ return base::StringPrintf("term signal %d", WTERMSIG(wstatus));
}
- Status sendOnewayCallback(const std::string& value) { return sendCallback(value); }
-
-public:
- std::mutex mMutex;
- std::condition_variable mCv;
- std::vector<std::string> mValues;
-};
-
-class MyBinderRpcTest : public BnBinderRpcTest {
-public:
- wp<RpcServer> server;
- int port = 0;
-
- Status sendString(const std::string& str) override {
- (void)str;
- return Status::ok();
- }
- Status doubleString(const std::string& str, std::string* strstr) override {
- *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) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
- out->clear();
- for (auto session : spServer->listSessions()) {
- size_t count = session->state()->countBinders();
- out->push_back(count);
- }
- return Status::ok();
- }
- Status pingMe(const sp<IBinder>& binder, int32_t* out) override {
- if (binder == nullptr) {
- std::cout << "Received null binder!" << std::endl;
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
- *out = binder->pingBinder();
- return Status::ok();
- }
- Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override {
- *out = binder;
- return Status::ok();
- }
- static sp<IBinder> mHeldBinder;
- Status holdBinder(const sp<IBinder>& binder) override {
- mHeldBinder = binder;
- return Status::ok();
- }
- Status getHeldBinder(sp<IBinder>* held) override {
- *held = mHeldBinder;
- return Status::ok();
- }
- Status nestMe(const sp<IBinderRpcTest>& binder, int count) override {
- if (count <= 0) return Status::ok();
- return binder->nestMe(this, count - 1);
- }
- Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override {
- static sp<IBinder> binder = new BBinder;
- *out = binder;
- return Status::ok();
- }
- Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override {
- *out = new MyBinderRpcSession(name);
- return Status::ok();
- }
- Status getNumOpenSessions(int32_t* out) override {
- *out = MyBinderRpcSession::gNum;
- return Status::ok();
- }
-
- std::mutex blockMutex;
- Status lock() override {
- blockMutex.lock();
- return Status::ok();
- }
- Status unlockInMsAsync(int32_t ms) override {
- usleep(ms * 1000);
- blockMutex.unlock();
- return Status::ok();
- }
- Status lockUnlock() override {
- std::lock_guard<std::mutex> _l(blockMutex);
- return Status::ok();
- }
-
- Status sleepMs(int32_t ms) override {
- usleep(ms * 1000);
- return Status::ok();
- }
-
- Status sleepMsAsync(int32_t ms) override {
- // In-process binder calls are asynchronous, but the call to this method
- // is synchronous wrt its client. This in/out-process threading model
- // diffentiation is a classic binder leaky abstraction (for better or
- // worse) and is preserved here the way binder sockets plugs itself
- // into BpBinder, as nothing is changed at the higher levels
- // (IInterface) which result in this behavior.
- return sleepMs(ms);
- }
-
- Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
- const std::string& value) override {
- if (callback == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
-
- if (delayed) {
- std::thread([=]() {
- ALOGE("Executing delayed callback: '%s'", value.c_str());
- Status status = doCallback(callback, oneway, false, value);
- ALOGE("Delayed callback status: '%s'", status.toString8().c_str());
- }).detach();
- return Status::ok();
- }
-
- if (oneway) {
- return callback->sendOnewayCallback(value);
- }
-
- return callback->sendCallback(value);
- }
-
- Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
- const std::string& value) override {
- return doCallback(callback, oneway, delayed, value);
- }
-
- Status die(bool cleanup) override {
- if (cleanup) {
- exit(1);
- } else {
- _exit(1);
- }
- }
-
- Status scheduleShutdown() override {
- sp<RpcServer> strongServer = server.promote();
- if (strongServer == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
- std::thread([=] {
- LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown");
- }).detach();
- return Status::ok();
- }
-
- Status useKernelBinderCallingId() override {
- // this is WRONG! It does not make sense when using RPC binder, and
- // because it is SO wrong, and so much code calls this, it should abort!
-
- (void)IPCThreadState::self()->getCallingPid();
- return Status::ok();
- }
-};
-sp<IBinder> MyBinderRpcTest::mHeldBinder;
+ return base::StringPrintf("unexpected state %d", wstatus);
+}
class Process {
public:
@@ -334,8 +113,8 @@
android::base::borrowed_fd /* readEnd */)>& f) {
android::base::unique_fd childWriteEnd;
android::base::unique_fd childReadEnd;
- CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno);
- CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno);
+ CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno);
+ CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno);
if (0 == (mPid = fork())) {
// racey: assume parent doesn't crash before this is set
prctl(PR_SET_PDEATHSIG, SIGHUP);
@@ -347,13 +126,28 @@
}
~Process() {
if (mPid != 0) {
- waitpid(mPid, nullptr, 0);
+ int wstatus;
+ waitpid(mPid, &wstatus, 0);
+ if (mCustomExitStatusCheck) {
+ mCustomExitStatusCheck(wstatus);
+ } else {
+ EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)
+ << "server process failed: " << WaitStatusToString(wstatus);
+ }
}
}
android::base::borrowed_fd readEnd() { return mReadEnd; }
android::base::borrowed_fd writeEnd() { return mWriteEnd; }
+ void setCustomExitStatusCheck(std::function<void(int wstatus)> f) {
+ mCustomExitStatusCheck = std::move(f);
+ }
+
+ // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
+ void terminate() { kill(mPid, SIGTERM); }
+
private:
+ std::function<void(int wstatus)> mCustomExitStatusCheck;
pid_t mPid = 0;
android::base::unique_fd mReadEnd;
android::base::unique_fd mWriteEnd;
@@ -368,7 +162,7 @@
};
static unsigned int allocateVsockPort() {
- static unsigned int vsockPort = 3456;
+ static unsigned int vsockPort = 34567;
return vsockPort++;
}
@@ -421,10 +215,10 @@
BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
~BinderRpcTestProcessSession() {
- EXPECT_NE(nullptr, rootIface);
- if (rootIface == nullptr) return;
-
if (!expectAlreadyShutdown) {
+ EXPECT_NE(nullptr, rootIface);
+ if (rootIface == nullptr) return;
+
std::vector<int32_t> remoteCounts;
// calling over any sessions counts across all sessions
EXPECT_OK(rootIface->countBinders(&remoteCounts));
@@ -445,28 +239,6 @@
}
};
-enum class SocketType {
- PRECONNECTED,
- UNIX,
- VSOCK,
- INET,
-};
-static inline std::string PrintToString(SocketType socketType) {
- switch (socketType) {
- case SocketType::PRECONNECTED:
- return "preconnected_uds";
- case SocketType::UNIX:
- return "unix_domain_socket";
- case SocketType::VSOCK:
- return "vm_socket";
- case SocketType::INET:
- return "inet_socket";
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- return "";
- }
-}
-
static base::unique_fd connectTo(const RpcSocketAddress& addr) {
base::unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
@@ -482,120 +254,79 @@
return serverFd;
}
-class BinderRpc
- : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t>> {
+using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd,
+ android::base::borrowed_fd readEnd);
+
+class BinderRpc : public ::testing::TestWithParam<
+ std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
public:
- struct Options {
- size_t numThreads = 1;
- size_t numSessions = 1;
- size_t numIncomingConnections = 0;
- size_t numOutgoingConnections = SIZE_MAX;
- };
+ SocketType socketType() const { return std::get<0>(GetParam()); }
+ RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
+ uint32_t clientVersion() const { return std::get<2>(GetParam()); }
+ uint32_t serverVersion() const { return std::get<3>(GetParam()); }
+ bool singleThreaded() const { return std::get<4>(GetParam()); }
+ bool noKernel() const { return std::get<5>(GetParam()); }
+
+ // Whether the test params support sending FDs in parcels.
+ bool supportsFdTransport() const {
+ return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
+ (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX);
+ }
static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [type, security, clientVersion, serverVersion] = info.param;
- return PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
+ auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
+ auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
- }
-
- static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
- uint64_t length = str.length();
- CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
- CHECK(android::base::WriteFully(fd, str.data(), str.length()));
- }
-
- static inline std::string readString(android::base::borrowed_fd fd) {
- uint64_t length;
- CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
- std::string ret(length, '\0');
- CHECK(android::base::ReadFully(fd, ret.data(), length));
+ if (singleThreaded) {
+ ret += "_single_threaded";
+ }
+ if (noKernel) {
+ ret += "_no_kernel";
+ }
return ret;
}
- static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
- Parcel parcel;
- CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
- writeString(fd,
- std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
- }
-
- template <typename T>
- static inline T readFromFd(android::base::borrowed_fd fd) {
- std::string data = readString(fd);
- Parcel parcel;
- CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
- T object;
- CHECK_EQ(OK, object.readFromParcel(&parcel));
- return object;
- }
-
// This creates a new process serving an interface on a certain number of
// threads.
- ProcessSession createRpcTestSocketServerProcess(
- const Options& options, const std::function<void(const sp<RpcServer>&)>& configure) {
+ ProcessSession createRpcTestSocketServerProcessEtc(const BinderRpcOptions& options) {
CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
SocketType socketType = std::get<0>(GetParam());
RpcSecurity rpcSecurity = std::get<1>(GetParam());
uint32_t clientVersion = std::get<2>(GetParam());
uint32_t serverVersion = std::get<3>(GetParam());
+ bool singleThreaded = std::get<4>(GetParam());
+ bool noKernel = std::get<5>(GetParam());
- unsigned int vsockPort = allocateVsockPort();
- std::string addr = allocateSocketAddress();
+ std::string path = android::base::GetExecutableDirectory();
+ auto servicePath =
+ android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
+ singleThreaded ? "_single_threaded" : "",
+ noKernel ? "_no_kernel" : "");
auto ret = ProcessSession{
.host = Process([=](android::base::borrowed_fd writeEnd,
android::base::borrowed_fd readEnd) {
- auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
- sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
-
- server->setProtocolVersion(serverVersion);
- server->setMaxThreads(options.numThreads);
-
- unsigned int outPort = 0;
-
- switch (socketType) {
- case SocketType::PRECONNECTED:
- [[fallthrough]];
- case SocketType::UNIX:
- CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())) << addr;
- break;
- case SocketType::VSOCK:
- CHECK_EQ(OK, server->setupVsockServer(vsockPort));
- break;
- case SocketType::INET: {
- CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
- CHECK_NE(0, outPort);
- break;
- }
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- }
-
- BinderRpcTestServerInfo serverInfo;
- serverInfo.port = static_cast<int64_t>(outPort);
- serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM);
- writeToFd(writeEnd, serverInfo);
- auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd);
-
- if (rpcSecurity == RpcSecurity::TLS) {
- for (const auto& clientCert : clientInfo.certs) {
- CHECK_EQ(OK,
- certVerifier
- ->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
- clientCert.data));
- }
- }
-
- configure(server);
-
- server->join();
-
- // Another thread calls shutdown. Wait for it to complete.
- (void)server->shutdown();
+ auto writeFd = std::to_string(writeEnd.get());
+ auto readFd = std::to_string(readEnd.get());
+ execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
+ NULL);
}),
};
+ BinderRpcTestServerConfig serverConfig;
+ serverConfig.numThreads = options.numThreads;
+ serverConfig.socketType = static_cast<int32_t>(socketType);
+ serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity);
+ serverConfig.serverVersion = serverVersion;
+ serverConfig.vsockPort = allocateVsockPort();
+ serverConfig.addr = allocateSocketAddress();
+ for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
+ serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
+ static_cast<int32_t>(mode));
+ }
+ writeToFd(ret.host.writeEnd(), serverConfig);
+
std::vector<sp<RpcSession>> sessions;
auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
for (size_t i = 0; i < options.numSessions; i++) {
@@ -628,18 +359,19 @@
CHECK(session->setProtocolVersion(clientVersion));
session->setMaxIncomingThreads(options.numIncomingConnections);
session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
switch (socketType) {
case SocketType::PRECONNECTED:
status = session->setupPreconnectedClient({}, [=]() {
- return connectTo(UnixSocketAddress(addr.c_str()));
+ return connectTo(UnixSocketAddress(serverConfig.addr.c_str()));
});
break;
case SocketType::UNIX:
- status = session->setupUnixDomainClient(addr.c_str());
+ status = session->setupUnixDomainClient(serverConfig.addr.c_str());
break;
case SocketType::VSOCK:
- status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
+ status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
break;
case SocketType::INET:
status = session->setupInetClient("127.0.0.1", serverInfo.port);
@@ -647,55 +379,22 @@
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
+ if (options.allowConnectFailure && status != OK) {
+ ret.sessions.clear();
+ break;
+ }
CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
ret.sessions.push_back({session, session->getRootObject()});
}
return ret;
}
- BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) {
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
BinderRpcTestProcessSession ret{
- .proc = createRpcTestSocketServerProcess(
- options,
- [&](const sp<RpcServer>& server) {
- server->setPerSessionRootObject([&](const void* addrPtr, size_t len) {
- // UNIX sockets with abstract addresses return
- // sizeof(sa_family_t)==2 in addrlen
- CHECK_GE(len, sizeof(sa_family_t));
- const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr);
- 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;
- });
- }),
+ .proc = createRpcTestSocketServerProcessEtc(options),
};
- ret.rootBinder = ret.proc.sessions.at(0).root;
+ ret.rootBinder = ret.proc.sessions.empty() ? nullptr : ret.proc.sessions.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
return ret;
@@ -705,6 +404,18 @@
size_t sleepMs = 500);
};
+// Test fixture for tests that start multiple threads.
+// This includes tests with one thread but multiple sessions,
+// since a server uses one thread per session.
+class BinderRpcThreads : public BinderRpc {
+public:
+ void SetUp() override {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+ }
+};
+
TEST_P(BinderRpc, Ping) {
auto proc = createRpcTestSocketServerProcess({});
ASSERT_NE(proc.rootBinder, nullptr);
@@ -717,7 +428,7 @@
EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
}
-TEST_P(BinderRpc, MultipleSessions) {
+TEST_P(BinderRpcThreads, MultipleSessions) {
auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
for (auto session : proc.proc.sessions) {
ASSERT_NE(nullptr, session.root);
@@ -725,7 +436,7 @@
}
}
-TEST_P(BinderRpc, SeparateRootObject) {
+TEST_P(BinderRpcThreads, SeparateRootObject) {
SocketType type = std::get<0>(GetParam());
if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) {
// we can't get port numbers for unix sockets
@@ -802,6 +513,13 @@
EXPECT_EQ(single + single, doubled);
}
+TEST_P(BinderRpc, InvalidNullBinderReturn) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
+}
+
TEST_P(BinderRpc, CallMeBack) {
auto proc = createRpcTestSocketServerProcess({});
@@ -901,7 +619,7 @@
proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
}
-TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+TEST_P(BinderRpcThreads, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
sp<IBinder> outBinder;
@@ -911,6 +629,11 @@
}
TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
@@ -920,6 +643,11 @@
}
TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
// for historical reasons, IServiceManager interface only returns the
@@ -939,7 +667,13 @@
}
TEST_P(BinderRpc, NestedTransactions) {
- auto proc = createRpcTestSocketServerProcess({});
+ auto proc = createRpcTestSocketServerProcess({
+ // Enable FD support because it uses more stack space and so represents
+ // something closer to a worst case scenario.
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
auto nastyNester = sp<MyBinderRpcTest>::make();
EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
@@ -1041,7 +775,7 @@
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
-TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+TEST_P(BinderRpcThreads, ThreadPoolGreaterThanEqualRequested) {
constexpr size_t kNumThreads = 10;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
@@ -1092,14 +826,14 @@
EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs);
}
-TEST_P(BinderRpc, ThreadPoolOverSaturated) {
+TEST_P(BinderRpcThreads, 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) {
+TEST_P(BinderRpcThreads, ThreadPoolLimitOutgoing) {
constexpr size_t kNumThreads = 20;
constexpr size_t kNumOutgoingConnections = 10;
constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
@@ -1108,7 +842,7 @@
testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
}
-TEST_P(BinderRpc, ThreadingStressTest) {
+TEST_P(BinderRpcThreads, ThreadingStressTest) {
constexpr size_t kNumClientThreads = 10;
constexpr size_t kNumServerThreads = 10;
constexpr size_t kNumCalls = 100;
@@ -1137,7 +871,7 @@
for (auto& t : threads) t.join();
}
-TEST_P(BinderRpc, OnewayStressTest) {
+TEST_P(BinderRpcThreads, OnewayStressTest) {
constexpr size_t kNumClientThreads = 10;
constexpr size_t kNumServerThreads = 10;
constexpr size_t kNumCalls = 1000;
@@ -1172,7 +906,7 @@
EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
}
-TEST_P(BinderRpc, OnewayCallQueueing) {
+TEST_P(BinderRpcThreads, OnewayCallQueueing) {
constexpr size_t kNumSleeps = 10;
constexpr size_t kNumExtraServerThreads = 4;
constexpr size_t kSleepMs = 50;
@@ -1201,7 +935,7 @@
saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
}
-TEST_P(BinderRpc, OnewayCallExhaustion) {
+TEST_P(BinderRpcThreads, OnewayCallExhaustion) {
constexpr size_t kNumClients = 2;
constexpr size_t kTooLongMs = 1000;
@@ -1244,11 +978,21 @@
TEST_P(BinderRpc, Callbacks) {
const static std::string kTestString = "good afternoon!";
+ bool bothSingleThreaded = !kEnableRpcThreads || singleThreaded();
+
for (bool callIsOneway : {true, false}) {
for (bool callbackIsOneway : {true, false}) {
for (bool delayed : {true, false}) {
+ if (bothSingleThreaded && (callIsOneway || callbackIsOneway || delayed)) {
+ // we have no incoming connections to receive the callback
+ continue;
+ }
+
+ size_t numIncomingConnections = bothSingleThreaded ? 0 : 1;
auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+ {.numThreads = 1,
+ .numSessions = 1,
+ .numIncomingConnections = numIncomingConnections});
auto cb = sp<MyBinderRpcCallback>::make();
if (callIsOneway) {
@@ -1264,7 +1008,7 @@
// the callback will be processed on another thread.
if (callIsOneway || callbackIsOneway || delayed) {
using std::literals::chrono_literals::operator""s;
- std::unique_lock<std::mutex> _l(cb->mMutex);
+ RpcMutexUniqueLock _l(cb->mMutex);
cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
}
@@ -1286,6 +1030,12 @@
// need to manually shut it down
EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ // Flaky. Sometimes gets SIGABRT.
+ EXPECT_TRUE((WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) ||
+ (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT))
+ << "server process failed: " << WaitStatusToString(wstatus);
+ });
proc.expectAlreadyShutdown = true;
}
}
@@ -1315,33 +1065,185 @@
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
<< "Do death cleanup: " << doDeathCleanup;
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
proc.expectAlreadyShutdown = true;
}
}
TEST_P(BinderRpc, UseKernelBinderCallingId) {
- bool okToFork = ProcessState::selfOrNull() == nullptr;
+ // This test only works if the current process shared the internal state of
+ // ProcessState with the service across the call to fork(). Both the static
+ // libraries and libbinder.so have their own separate copies of all the
+ // globals, so the test only works when the test client and service both use
+ // libbinder.so (when using static libraries, even a client and service
+ // using the same kind of static library should have separate copies of the
+ // variables).
+ if (!kEnableSharedLibs || singleThreaded() || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
auto proc = createRpcTestSocketServerProcess({});
- // 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());
- }
+ // 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());
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
proc.expectAlreadyShutdown = true;
}
+TEST_P(BinderRpc, FileDescriptorTransportRejectNone) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ .allowConnectFailure = true,
+ });
+ EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
+ proc.proc.host.terminate();
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
+ proc.expectAlreadyShutdown = true;
+}
+
+TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::NONE},
+ .allowConnectFailure = true,
+ });
+ EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
+ proc.proc.host.terminate();
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
+ proc.expectAlreadyShutdown = true;
+}
+
+TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::NONE,
+ RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->echoAsFile("hello", &out);
+ EXPECT_EQ(status.transactionError(), FDS_NOT_ALLOWED) << status;
+}
+
+TEST_P(BinderRpc, ReceiveFile) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->echoAsFile("hello", &out);
+ if (!supportsFdTransport()) {
+ EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
+ return;
+ }
+ ASSERT_TRUE(status.isOk()) << status;
+
+ std::string result;
+ CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_EQ(result, "hello");
+}
+
+TEST_P(BinderRpc, SendFiles) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ std::vector<android::os::ParcelFileDescriptor> files;
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("123")));
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a")));
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("b")));
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("cd")));
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->concatFiles(files, &out);
+ if (!supportsFdTransport()) {
+ EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
+ return;
+ }
+ ASSERT_TRUE(status.isOk()) << status;
+
+ std::string result;
+ CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_EQ(result, "123abcd");
+}
+
+TEST_P(BinderRpc, SendMaxFiles) {
+ if (!supportsFdTransport()) {
+ GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ std::vector<android::os::ParcelFileDescriptor> files;
+ for (int i = 0; i < 253; i++) {
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a")));
+ }
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->concatFiles(files, &out);
+ ASSERT_TRUE(status.isOk()) << status;
+
+ std::string result;
+ CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_EQ(result, std::string(253, 'a'));
+}
+
+TEST_P(BinderRpc, SendTooManyFiles) {
+ if (!supportsFdTransport()) {
+ GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ std::vector<android::os::ParcelFileDescriptor> files;
+ for (int i = 0; i < 254; i++) {
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a")));
+ }
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->concatFiles(files, &out);
+ EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
+}
+
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
+ if constexpr (!kEnableSharedLibs) {
+ GTEST_SKIP() << "Test disabled because Binder was built as a static library";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
@@ -1351,6 +1253,10 @@
}
TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) {
+ if constexpr (!kEnableSharedLibs) {
+ GTEST_SKIP() << "Test disabled because Binder was built as a static library";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
@@ -1375,7 +1281,7 @@
return ret;
}
-TEST_P(BinderRpc, Fds) {
+TEST_P(BinderRpcThreads, Fds) {
ssize_t beforeFds = countFds();
ASSERT_GE(beforeFds, 0);
{
@@ -1398,20 +1304,90 @@
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());
- if (status_t status = server->setupVsockServer(vsockPort); status != OK) {
- if (status == -EAFNOSUPPORT) {
- return false;
- }
- LOG_ALWAYS_FATAL("Could not setup vsock server: %s", statusToString(status).c_str());
- }
- server->start();
- sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
- status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
- while (!server->shutdown()) usleep(10000);
- ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str());
- return status == OK;
+ android::base::unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+ LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno));
+
+ sockaddr_vm serverAddr{
+ .svm_family = AF_VSOCK,
+ .svm_port = vsockPort,
+ .svm_cid = VMADDR_CID_ANY,
+ };
+ int ret = TEMP_FAILURE_RETRY(
+ bind(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr)));
+ LOG_ALWAYS_FATAL_IF(0 != ret, "Could not bind socket to port %u: %s", vsockPort,
+ strerror(errno));
+
+ ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/));
+ LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", vsockPort,
+ strerror(errno));
+
+ // Try to connect to the server using the VMADDR_CID_LOCAL cid
+ // to see if the kernel supports it. It's safe to use a blocking
+ // connect because vsock sockets have a 2 second connection timeout,
+ // and they return ETIMEDOUT after that.
+ android::base::unique_fd connectFd(
+ TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+ LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort,
+ strerror(errno));
+
+ bool success = false;
+ sockaddr_vm connectAddr{
+ .svm_family = AF_VSOCK,
+ .svm_port = vsockPort,
+ .svm_cid = VMADDR_CID_LOCAL,
+ };
+ ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast<sockaddr*>(&connectAddr),
+ sizeof(connectAddr)));
+ if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) {
+ android::base::unique_fd acceptFd;
+ while (true) {
+ pollfd pfd[]{
+ {.fd = serverFd.get(), .events = POLLIN, .revents = 0},
+ {.fd = connectFd.get(), .events = POLLOUT, .revents = 0},
+ };
+ ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+ LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno));
+
+ if (pfd[0].revents & POLLIN) {
+ sockaddr_vm acceptAddr;
+ socklen_t acceptAddrLen = sizeof(acceptAddr);
+ ret = TEMP_FAILURE_RETRY(accept4(serverFd.get(),
+ reinterpret_cast<sockaddr*>(&acceptAddr),
+ &acceptAddrLen, SOCK_CLOEXEC));
+ LOG_ALWAYS_FATAL_IF(ret < 0, "Could not accept4 socket: %s", strerror(errno));
+ LOG_ALWAYS_FATAL_IF(acceptAddrLen != static_cast<socklen_t>(sizeof(acceptAddr)),
+ "Truncated address");
+
+ // Store the fd in acceptFd so we keep the connection alive
+ // while polling connectFd
+ acceptFd.reset(ret);
+ }
+
+ if (pfd[1].revents & POLLOUT) {
+ // Connect either succeeded or timed out
+ int connectErrno;
+ socklen_t connectErrnoLen = sizeof(connectErrno);
+ int ret = getsockopt(connectFd.get(), SOL_SOCKET, SO_ERROR, &connectErrno,
+ &connectErrnoLen);
+ LOG_ALWAYS_FATAL_IF(ret == -1,
+ "Could not getsockopt() after connect() "
+ "on non-blocking socket: %s.",
+ strerror(errno));
+
+ // We're done, this is all we wanted
+ success = connectErrno == 0;
+ break;
+ }
+ }
+ } else {
+ success = ret == 0;
+ }
+
+ ALOGE("Detected vsock loopback supported: %s", success ? "yes" : "no");
+
+ return success;
}
static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
@@ -1441,7 +1417,18 @@
::testing::Combine(::testing::ValuesIn(testSocketTypes()),
::testing::ValuesIn(RpcSecurityValues()),
::testing::ValuesIn(testVersions()),
- ::testing::ValuesIn(testVersions())),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::Values(false, true),
+ ::testing::Values(false, true)),
+ BinderRpc::PrintParamInfo);
+
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpcThreads,
+ ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
+ ::testing::ValuesIn(RpcSecurityValues()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::Values(false),
+ ::testing::Values(false, true)),
BinderRpc::PrintParamInfo);
class BinderRpcServerRootObject
@@ -1496,6 +1483,10 @@
};
TEST_P(BinderRpcServerOnly, Shutdown) {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+
auto addr = allocateSocketAddress();
auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
server->setProtocolVersion(std::get<1>(GetParam()));
@@ -1527,6 +1518,11 @@
"createRpcDelegateServiceManager() with a device attached, such test belongs "
"to binderHostDeviceTest. Hence, just disable this test on host.";
#endif // !__ANDROID__
+ if constexpr (!kEnableKernelIpc) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
sp<IServiceManager> sm = defaultServiceManager();
ASSERT_NE(nullptr, sm);
// Any Java service with non-empty getInterfaceDescriptor() would do.
@@ -1711,7 +1707,7 @@
std::string message(kMessage);
iovec messageIov{message.data(), message.size()};
auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1,
- std::nullopt);
+ std::nullopt, nullptr);
if (status != OK) return AssertionFailure() << statusToString(status);
return AssertionSuccess();
}
@@ -1746,7 +1742,7 @@
iovec readMessageIov{readMessage.data(), readMessage.size()};
status_t readStatus =
mClientTransport->interruptableReadFully(mFdTrigger.get(), &readMessageIov, 1,
- std::nullopt);
+ std::nullopt, nullptr);
if (readStatus != OK) {
return AssertionFailure() << statusToString(readStatus);
}
@@ -1827,6 +1823,11 @@
(void)serverVersion;
return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b);
}
+ void SetUp() override {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+ }
};
TEST_P(RpcTransportTest, GoodCertificate) {
@@ -1954,8 +1955,8 @@
auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
std::string message(RpcTransportTestUtils::kMessage);
iovec messageIov{message.data(), message.size()};
- auto status =
- serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, std::nullopt);
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1,
+ std::nullopt, nullptr);
if (status != OK) return AssertionFailure() << statusToString(status);
{
@@ -1966,7 +1967,8 @@
}
iovec msg2Iov{msg2.data(), msg2.size()};
- status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt);
+ status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt,
+ nullptr);
if (status != DEAD_OBJECT)
return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
"should return DEAD_OBJECT, but it is "
@@ -2030,6 +2032,10 @@
};
TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+
auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam();
std::vector<uint8_t> pkeyData, certData;
diff --git a/libs/binder/tests/binderRpcTestCommon.cpp b/libs/binder/tests/binderRpcTestCommon.cpp
new file mode 100644
index 0000000..0d9aa95
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestCommon.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "binderRpcTestCommon.h"
+
+namespace android {
+
+std::atomic<int32_t> MyBinderRpcSession::gNum;
+sp<IBinder> MyBinderRpcTest::mHeldBinder;
+
+} // namespace android
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
new file mode 100644
index 0000000..4513d36
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <BinderRpcTestClientInfo.h>
+#include <BinderRpcTestServerConfig.h>
+#include <BinderRpcTestServerInfo.h>
+#include <BnBinderRpcCallback.h>
+#include <BnBinderRpcSession.h>
+#include <BnBinderRpcTest.h>
+#include <aidl/IBinderRpcTest.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_libbinder.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcThreads.h>
+#include <binder/RpcTlsTestUtils.h>
+#include <binder/RpcTlsUtils.h>
+#include <binder/RpcTransport.h>
+#include <binder/RpcTransportRaw.h>
+#include <binder/RpcTransportTls.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <signal.h>
+
+#include "../BuildFlags.h"
+#include "../FdTrigger.h"
+#include "../RpcSocketAddress.h" // for testing preconnected clients
+#include "../RpcState.h" // for debugging
+#include "../vm_sockets.h" // for VMADDR_*
+#include "utils/Errors.h"
+
+namespace android {
+
+constexpr char kLocalInetAddress[] = "127.0.0.1";
+
+enum class RpcSecurity { RAW, TLS };
+
+static inline std::vector<RpcSecurity> RpcSecurityValues() {
+ return {RpcSecurity::RAW, RpcSecurity::TLS};
+}
+
+enum class SocketType {
+ PRECONNECTED,
+ UNIX,
+ VSOCK,
+ INET,
+};
+static inline std::string PrintToString(SocketType socketType) {
+ switch (socketType) {
+ case SocketType::PRECONNECTED:
+ return "preconnected_uds";
+ case SocketType::UNIX:
+ return "unix_domain_socket";
+ case SocketType::VSOCK:
+ return "vm_socket";
+ case SocketType::INET:
+ return "inet_socket";
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ return "";
+ }
+}
+
+struct BinderRpcOptions {
+ size_t numThreads = 1;
+ size_t numSessions = 1;
+ size_t numIncomingConnections = 0;
+ size_t numOutgoingConnections = SIZE_MAX;
+ RpcSession::FileDescriptorTransportMode clientFileDescriptorTransportMode =
+ RpcSession::FileDescriptorTransportMode::NONE;
+ std::vector<RpcSession::FileDescriptorTransportMode>
+ serverSupportedFileDescriptorTransportModes = {
+ RpcSession::FileDescriptorTransportMode::NONE};
+
+ // If true, connection failures will result in `ProcessSession::sessions` being empty
+ // instead of a fatal error.
+ bool allowConnectFailure = false;
+};
+
+static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
+ uint64_t length = str.length();
+ CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
+ CHECK(android::base::WriteFully(fd, str.data(), str.length()));
+}
+
+static inline std::string readString(android::base::borrowed_fd fd) {
+ uint64_t length;
+ CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
+ std::string ret(length, '\0');
+ CHECK(android::base::ReadFully(fd, ret.data(), length));
+ return ret;
+}
+
+static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
+ Parcel parcel;
+ CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
+ writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
+}
+
+template <typename T>
+static inline T readFromFd(android::base::borrowed_fd fd) {
+ std::string data = readString(fd);
+ Parcel parcel;
+ CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+ T object;
+ CHECK_EQ(OK, object.readFromParcel(&parcel));
+ return object;
+}
+
+static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
+ RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr,
+ std::unique_ptr<RpcAuth> auth = nullptr) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW:
+ return RpcTransportCtxFactoryRaw::make();
+ case RpcSecurity::TLS: {
+ if (verifier == nullptr) {
+ verifier = std::make_shared<RpcCertificateVerifierSimple>();
+ }
+ if (auth == nullptr) {
+ auth = std::make_unique<RpcAuthSelfSigned>();
+ }
+ return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth));
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
+ }
+}
+
+// Create an FD that returns `contents` when read.
+static inline base::unique_fd mockFileDescriptor(std::string contents) {
+ android::base::unique_fd readFd, writeFd;
+ CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno);
+ RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() {
+ signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write
+ if (!WriteStringToFd(contents, writeFd)) {
+ int savedErrno = errno;
+ LOG_ALWAYS_FATAL_IF(EPIPE != savedErrno, "mockFileDescriptor write failed: %s",
+ strerror(savedErrno));
+ }
+ }).detach();
+ return readFd;
+}
+
+using android::binder::Status;
+
+class MyBinderRpcSession : public BnBinderRpcSession {
+public:
+ static std::atomic<int32_t> gNum;
+
+ MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; }
+ Status getName(std::string* name) override {
+ *name = mName;
+ return Status::ok();
+ }
+ ~MyBinderRpcSession() { gNum--; }
+
+private:
+ std::string mName;
+};
+
+class MyBinderRpcCallback : public BnBinderRpcCallback {
+ Status sendCallback(const std::string& value) {
+ RpcMutexUniqueLock _l(mMutex);
+ mValues.push_back(value);
+ _l.unlock();
+ mCv.notify_one();
+ return Status::ok();
+ }
+ Status sendOnewayCallback(const std::string& value) { return sendCallback(value); }
+
+public:
+ RpcMutex mMutex;
+ RpcConditionVariable mCv;
+ std::vector<std::string> mValues;
+};
+
+class MyBinderRpcTest : public BnBinderRpcTest {
+public:
+ wp<RpcServer> server;
+ int port = 0;
+
+ Status sendString(const std::string& str) override {
+ (void)str;
+ return Status::ok();
+ }
+ Status doubleString(const std::string& str, std::string* strstr) override {
+ *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) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ out->clear();
+ for (auto session : spServer->listSessions()) {
+ size_t count = session->state()->countBinders();
+ out->push_back(count);
+ }
+ return Status::ok();
+ }
+ Status getNullBinder(sp<IBinder>* out) override {
+ out->clear();
+ return Status::ok();
+ }
+ Status pingMe(const sp<IBinder>& binder, int32_t* out) override {
+ if (binder == nullptr) {
+ std::cout << "Received null binder!" << std::endl;
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ *out = binder->pingBinder();
+ return Status::ok();
+ }
+ Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override {
+ *out = binder;
+ return Status::ok();
+ }
+ static sp<IBinder> mHeldBinder;
+ Status holdBinder(const sp<IBinder>& binder) override {
+ mHeldBinder = binder;
+ return Status::ok();
+ }
+ Status getHeldBinder(sp<IBinder>* held) override {
+ *held = mHeldBinder;
+ return Status::ok();
+ }
+ Status nestMe(const sp<IBinderRpcTest>& binder, int count) override {
+ if (count <= 0) return Status::ok();
+ return binder->nestMe(this, count - 1);
+ }
+ Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override {
+ static sp<IBinder> binder = new BBinder;
+ *out = binder;
+ return Status::ok();
+ }
+ Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override {
+ *out = new MyBinderRpcSession(name);
+ return Status::ok();
+ }
+ Status getNumOpenSessions(int32_t* out) override {
+ *out = MyBinderRpcSession::gNum;
+ return Status::ok();
+ }
+
+ RpcMutex blockMutex;
+ Status lock() override {
+ blockMutex.lock();
+ return Status::ok();
+ }
+ Status unlockInMsAsync(int32_t ms) override {
+ usleep(ms * 1000);
+ blockMutex.unlock();
+ return Status::ok();
+ }
+ Status lockUnlock() override {
+ RpcMutexLockGuard _l(blockMutex);
+ return Status::ok();
+ }
+
+ Status sleepMs(int32_t ms) override {
+ usleep(ms * 1000);
+ return Status::ok();
+ }
+
+ Status sleepMsAsync(int32_t ms) override {
+ // In-process binder calls are asynchronous, but the call to this method
+ // is synchronous wrt its client. This in/out-process threading model
+ // diffentiation is a classic binder leaky abstraction (for better or
+ // worse) and is preserved here the way binder sockets plugs itself
+ // into BpBinder, as nothing is changed at the higher levels
+ // (IInterface) which result in this behavior.
+ return sleepMs(ms);
+ }
+
+ Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
+ const std::string& value) override {
+ if (callback == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+
+ if (delayed) {
+ RpcMaybeThread([=]() {
+ ALOGE("Executing delayed callback: '%s'", value.c_str());
+ Status status = doCallback(callback, oneway, false, value);
+ ALOGE("Delayed callback status: '%s'", status.toString8().c_str());
+ }).detach();
+ return Status::ok();
+ }
+
+ if (oneway) {
+ return callback->sendOnewayCallback(value);
+ }
+
+ return callback->sendCallback(value);
+ }
+
+ Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
+ const std::string& value) override {
+ return doCallback(callback, oneway, delayed, value);
+ }
+
+ Status die(bool cleanup) override {
+ if (cleanup) {
+ exit(1);
+ } else {
+ _exit(1);
+ }
+ }
+
+ Status scheduleShutdown() override {
+ sp<RpcServer> strongServer = server.promote();
+ if (strongServer == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ RpcMaybeThread([=] {
+ LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown");
+ }).detach();
+ return Status::ok();
+ }
+
+ Status useKernelBinderCallingId() override {
+ // this is WRONG! It does not make sense when using RPC binder, and
+ // because it is SO wrong, and so much code calls this, it should abort!
+
+ if constexpr (kEnableKernelIpc) {
+ (void)IPCThreadState::self()->getCallingPid();
+ }
+ return Status::ok();
+ }
+
+ Status echoAsFile(const std::string& content, android::os::ParcelFileDescriptor* out) override {
+ out->reset(mockFileDescriptor(content));
+ return Status::ok();
+ }
+
+ Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& files,
+ android::os::ParcelFileDescriptor* out) override {
+ std::string acc;
+ for (const auto& file : files) {
+ std::string result;
+ CHECK(android::base::ReadFdToString(file.get(), &result));
+ acc.append(result);
+ }
+ out->reset(mockFileDescriptor(acc));
+ return Status::ok();
+ }
+};
+
+} // namespace android
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
new file mode 100644
index 0000000..31eb5da
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "binderRpcTestCommon.h"
+
+using namespace android;
+
+int main(int argc, const char* argv[]) {
+ LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
+ base::unique_fd writeEnd(atoi(argv[1]));
+ base::unique_fd readEnd(atoi(argv[2]));
+
+ auto serverConfig = readFromFd<BinderRpcTestServerConfig>(readEnd);
+ auto socketType = static_cast<SocketType>(serverConfig.socketType);
+ auto rpcSecurity = static_cast<RpcSecurity>(serverConfig.rpcSecurity);
+
+ std::vector<RpcSession::FileDescriptorTransportMode>
+ serverSupportedFileDescriptorTransportModes;
+ for (auto mode : serverConfig.serverSupportedFileDescriptorTransportModes) {
+ serverSupportedFileDescriptorTransportModes.push_back(
+ static_cast<RpcSession::FileDescriptorTransportMode>(mode));
+ }
+
+ auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+ sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
+
+ server->setProtocolVersion(serverConfig.serverVersion);
+ server->setMaxThreads(serverConfig.numThreads);
+ server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
+
+ unsigned int outPort = 0;
+
+ switch (socketType) {
+ case SocketType::PRECONNECTED:
+ [[fallthrough]];
+ case SocketType::UNIX:
+ CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str()))
+ << serverConfig.addr;
+ break;
+ case SocketType::VSOCK:
+ CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
+ break;
+ case SocketType::INET: {
+ CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
+ CHECK_NE(0, outPort);
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
+
+ BinderRpcTestServerInfo serverInfo;
+ serverInfo.port = static_cast<int64_t>(outPort);
+ serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM);
+ writeToFd(writeEnd, serverInfo);
+ auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd);
+
+ if (rpcSecurity == RpcSecurity::TLS) {
+ for (const auto& clientCert : clientInfo.certs) {
+ CHECK_EQ(OK,
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+ clientCert.data));
+ }
+ }
+
+ server->setPerSessionRootObject([&](const void* addrPtr, size_t len) {
+ // UNIX sockets with abstract addresses return
+ // sizeof(sa_family_t)==2 in addrlen
+ CHECK_GE(len, sizeof(sa_family_t));
+ const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr);
+ 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;
+ });
+
+ server->join();
+
+ // Another thread calls shutdown. Wait for it to complete.
+ (void)server->shutdown();
+
+ return 0;
+}
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 0f1a02a..a6585c5 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -85,7 +85,20 @@
/* list of hal interface to dump containing process during native dumps */
static const std::vector<std::string> aidl_interfaces_to_dump {
+ "android.hardware.automotive.audiocontrol.IAudioControl",
+ "android.hardware.automotive.evs.IEvsEnumerator",
+ "android.hardware.biometrics.face.IBiometricsFace",
+ "android.hardware.biometrics.fingerprint.IBiometricsFingerprint",
"android.hardware.camera.provider.ICameraProvider",
+ "android.hardware.drm.IDrmFactory",
+ "android.hardware.graphics.allocator.IAllocator",
+ "android.hardware.graphics.composer3.IComposer",
+ "android.hardware.health.IHealth",
+ "android.hardware.input.processor.IInputProcessor",
+ "android.hardware.neuralnetworks.IDevice",
+ "android.hardware.power.IPower",
+ "android.hardware.power.stats.IPowerStats",
+ "android.hardware.sensors.ISensors",
};
/* list of extra hal interfaces to dump containing process during native dumps */
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 9086f1c..d098e3a 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -354,7 +354,7 @@
},
whole_static_libs: [
- "LibGuiProperties",
+ "libLibGuiProperties",
],
shared_libs: [
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index f340614..2212e58 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -159,7 +159,6 @@
id++;
mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
- mBufferItemConsumer->setBufferFreedListener(this);
ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
@@ -288,18 +287,17 @@
// We need to check if we were waiting for a transaction callback in order to
// process any pending buffers and unblock. It's possible to get transaction
- // callbacks for previous requests so we need to ensure the frame from this
- // transaction callback matches the last acquired buffer. Since acquireNextBuffer
- // will stop processing buffers when mWaitForTransactionCallback is set, we know
- // that mLastAcquiredFrameNumber is the frame we're waiting on.
- // We also want to check if mNextTransaction is null because it's possible another
+ // callbacks for previous requests so we need to ensure that there are no pending
+ // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers
+ // set and then check if it's empty. If there are no more pending syncs, we can
+ // proceed with flushing the shadow queue.
+ // We also want to check if mSyncTransaction is null because it's possible another
// sync request came in while waiting, but it hasn't started processing yet. In that
// case, we don't actually want to flush the frames in between since they will get
// processed and merged with the sync transaction and released earlier than if they
// were sent to SF
- if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
- currFrameNumber >= mLastAcquiredFrameNumber) {
- mWaitForTransactionCallback = false;
+ mSyncedFrameNumbers.erase(currFrameNumber);
+ if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) {
flushShadowQueue();
}
} else {
@@ -417,9 +415,11 @@
const auto releasedBuffer = mPendingRelease.front();
mPendingRelease.pop_front();
releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
- // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
- // onFrameAvailable handle processing them since it will merge with the syncTransaction.
- if (!mWaitForTransactionCallback) {
+ // Don't process the transactions here if mSyncedFrameNumbers is not empty. That means
+ // are still transactions that have sync buffers in them that have not been applied or
+ // dropped. Instead, let onFrameAvailable handle processing them since it will merge with
+ // the syncTransaction.
+ if (mSyncedFrameNumbers.empty()) {
acquireNextBufferLocked(std::nullopt);
}
}
@@ -443,6 +443,9 @@
BQA_LOGV("released %s", callbackId.to_string().c_str());
mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
mSubmitted.erase(it);
+ // Remove the frame number from mSyncedFrameNumbers since we can get a release callback
+ // without getting a transaction committed if the buffer was dropped.
+ mSyncedFrameNumbers.erase(callbackId.framenumber);
}
void BLASTBufferQueue::acquireNextBufferLocked(
@@ -609,7 +612,7 @@
}
void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
- if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
+ if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) {
// We are waiting on a previous sync's transaction callback so allow another sync
// transaction to proceed.
//
@@ -636,6 +639,8 @@
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+ bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
+
{
BBQ_TRACE();
std::unique_lock _lock{mMutex};
@@ -667,7 +672,7 @@
// add to shadow queue
mNumFrameAvailable++;
- if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
+ if (waitForTransactionCallback && mNumFrameAvailable >= 2) {
acquireAndReleaseBuffer();
}
ATRACE_INT(mQueuedBufferTrace.c_str(),
@@ -684,14 +689,14 @@
incStrong((void*)transactionCommittedCallbackThunk);
mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
static_cast<void*>(this));
- mWaitForTransactionCallback = true;
+ mSyncedFrameNumbers.emplace(item.mFrameNumber);
if (mAcquireSingleBuffer) {
prevCallback = mTransactionReadyCallback;
prevTransaction = mSyncTransaction;
mTransactionReadyCallback = nullptr;
mSyncTransaction = nullptr;
}
- } else if (!mWaitForTransactionCallback) {
+ } else if (!waitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
}
}
@@ -1098,9 +1103,9 @@
}
// Clear sync states
- if (mWaitForTransactionCallback) {
- BQA_LOGD("mWaitForTransactionCallback cleared");
- mWaitForTransactionCallback = false;
+ if (!mSyncedFrameNumbers.empty()) {
+ BQA_LOGD("mSyncedFrameNumbers cleared");
+ mSyncedFrameNumbers.clear();
}
if (mSyncTransaction != nullptr) {
@@ -1114,7 +1119,6 @@
if (mBufferItemConsumer != nullptr) {
mBufferItemConsumer->abandon();
mBufferItemConsumer->setFrameAvailableListener(nullptr);
- mBufferItemConsumer->setBufferFreedListener(nullptr);
}
mBufferItemConsumer = nullptr;
mConsumer = nullptr;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 7f0f638..bb66085 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -63,6 +63,7 @@
frameRate(0.0f),
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
+ defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
isTrustedOverlay(false),
@@ -137,6 +138,7 @@
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
+ SAFE_PARCEL(output.writeByte, defaultFrameRateCompatibility);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeBool, autoRefresh);
SAFE_PARCEL(output.writeBool, dimmingEnabled);
@@ -257,6 +259,7 @@
SAFE_PARCEL(input.readFloat, &frameRate);
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
+ SAFE_PARCEL(input.readByte, &defaultFrameRateCompatibility);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readBool, &autoRefresh);
@@ -573,6 +576,10 @@
borderWidth = other.borderWidth;
borderColor = other.borderColor;
}
+ if (other.what & eDefaultFrameRateCompatibilityChanged) {
+ what |= eDefaultFrameRateCompatibilityChanged;
+ defaultFrameRateCompatibility = other.defaultFrameRateCompatibility;
+ }
if (other.what & eFrameRateSelectionPriority) {
what |= eFrameRateSelectionPriority;
frameRateSelectionPriority = other.frameRateSelectionPriority;
@@ -664,29 +671,44 @@
changes |= !other.focusRequests.empty();
focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()),
std::make_move_iterator(other.focusRequests.end()));
- changes |= other.syncInputWindows && !syncInputWindows;
- syncInputWindows |= other.syncInputWindows;
+ changes |= !other.windowInfosReportedListeners.empty();
+ windowInfosReportedListeners.insert(other.windowInfosReportedListeners.begin(),
+ other.windowInfosReportedListeners.end());
return changes;
}
bool InputWindowCommands::empty() const {
- return focusRequests.empty() && !syncInputWindows;
+ return focusRequests.empty() && windowInfosReportedListeners.empty();
}
void InputWindowCommands::clear() {
focusRequests.clear();
- syncInputWindows = false;
+ windowInfosReportedListeners.clear();
}
status_t InputWindowCommands::write(Parcel& output) const {
SAFE_PARCEL(output.writeParcelableVector, focusRequests);
- SAFE_PARCEL(output.writeBool, syncInputWindows);
+
+ SAFE_PARCEL(output.writeInt32, windowInfosReportedListeners.size());
+ for (const auto& listener : windowInfosReportedListeners) {
+ SAFE_PARCEL(output.writeStrongBinder, listener);
+ }
+
return NO_ERROR;
}
status_t InputWindowCommands::read(const Parcel& input) {
SAFE_PARCEL(input.readParcelableVector, &focusRequests);
- SAFE_PARCEL(input.readBool, &syncInputWindows);
+
+ int listenerSize = 0;
+ SAFE_PARCEL_READ_SIZE(input.readInt32, &listenerSize, input.dataSize());
+ windowInfosReportedListeners.reserve(listenerSize);
+ for (int i = 0; i < listenerSize; i++) {
+ sp<gui::IWindowInfosReportedListener> listener;
+ SAFE_PARCEL(input.readStrongBinder, &listener);
+ windowInfosReportedListeners.insert(listener);
+ }
+
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9bc159d..16dbc92 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/DisplayState.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <android/gui/IWindowInfosListener.h>
@@ -635,7 +636,8 @@
mDesiredPresentTime(other.mDesiredPresentTime),
mIsAutoTimestamp(other.mIsAutoTimestamp),
mFrameTimelineInfo(other.mFrameTimelineInfo),
- mApplyToken(other.mApplyToken) {
+ mApplyToken(other.mApplyToken),
+ mWindowInfosReportedEvent(other.mWindowInfosReportedEvent) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
@@ -660,6 +662,7 @@
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
+ const uint64_t transactionId = parcel->readUint64();
const uint32_t forceSynchronous = parcel->readUint32();
const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
@@ -737,6 +740,7 @@
inputWindowCommands.read(*parcel);
// Parsing was successful. Update the object.
+ mId = transactionId;
mForceSynchronous = forceSynchronous;
mTransactionNestCount = transactionNestCount;
mAnimation = animation;
@@ -768,6 +772,7 @@
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
+ parcel->writeUint64(mId);
parcel->writeUint32(mForceSynchronous);
parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
@@ -876,6 +881,9 @@
mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
mApplyToken = other.mApplyToken;
+ if (other.mWindowInfosReportedEvent) {
+ mWindowInfosReportedEvent = std::move(other.mWindowInfosReportedEvent);
+ }
mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
@@ -898,6 +906,7 @@
mIsAutoTimestamp = true;
clearFrameTimelineInfo(mFrameTimelineInfo);
mApplyToken = nullptr;
+ mWindowInfosReportedEvent = nullptr;
}
uint64_t SurfaceComposerClient::Transaction::getId() {
@@ -1044,6 +1053,10 @@
hasListenerCallbacks, listenerCallbacks, mId);
mId = generateId();
+ if (mWindowInfosReportedEvent && !mWindowInfosReportedEvent->wait()) {
+ ALOGE("Timed out waiting for window infos to be reported.");
+ }
+
// Clear the current states and flags
clear();
@@ -1730,8 +1743,25 @@
return *this;
}
+class NotifyWindowInfosReported : public gui::BnWindowInfosReportedListener {
+public:
+ NotifyWindowInfosReported(
+ std::shared_ptr<SurfaceComposerClient::Event> windowInfosReportedEvent)
+ : mWindowInfosReportedEvent(windowInfosReportedEvent) {}
+
+ binder::Status onWindowInfosReported() {
+ mWindowInfosReportedEvent->set();
+ return binder::Status::ok();
+ }
+
+private:
+ std::shared_ptr<SurfaceComposerClient::Event> mWindowInfosReportedEvent;
+};
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
- mInputWindowCommands.syncInputWindows = true;
+ mWindowInfosReportedEvent = std::make_shared<Event>();
+ mInputWindowCommands.windowInfosReportedListeners.insert(
+ sp<NotifyWindowInfosReported>::make(mWindowInfosReportedEvent));
return *this;
}
@@ -1838,6 +1868,19 @@
return *this;
}
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setDefaultFrameRateCompatibility(const sp<SurfaceControl>& sc,
+ int8_t compatibility) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eDefaultFrameRateCompatibilityChanged;
+ s->defaultFrameRateCompatibility = compatibility;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint(
const sp<SurfaceControl>& sc, int32_t fixedTransformHint) {
layer_state_t* s = getLayerState(sc);
@@ -2806,4 +2849,17 @@
}
}
+// ---------------------------------------------------------------------------------
+
+void SurfaceComposerClient::Event::set() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mComplete = true;
+ mConditionVariable.notify_all();
+}
+
+bool SurfaceComposerClient::Event::wait() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mConditionVariable.wait_for(lock, sTimeout, [this] { return mComplete; });
+}
+
} // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 9328a54..2ed5260 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -69,9 +69,7 @@
bool mPreviouslyConnected GUARDED_BY(mMutex);
};
-class BLASTBufferQueue
- : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
-{
+class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener {
public:
BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
@@ -83,7 +81,6 @@
sp<Surface> getSurface(bool includeSurfaceControlHandle);
bool isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const;
- void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
void onFrameReplaced(const BufferItem& item) override;
void onFrameAvailable(const BufferItem& item) override;
void onFrameDequeued(const uint64_t) override;
@@ -251,7 +248,6 @@
std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
uint32_t mCurrentMaxAcquiredBufferCount;
- bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
// Flag to determine if syncTransaction should only acquire a single buffer and then clear or
// continue to acquire buffers until explicitly cleared
@@ -279,6 +275,8 @@
uint64_t mLastAppliedFrameNumber = 0;
std::function<void(bool)> mTransactionHangCallback;
+
+ std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
};
} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 37a1595..f3701e8 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/IWindowInfosReportedListener.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ITransactionCompletedListener.h>
@@ -165,7 +166,7 @@
eTransformToDisplayInverseChanged = 0x00080000,
eCropChanged = 0x00100000,
eBufferChanged = 0x00200000,
- /* unused 0x00400000, */
+ eDefaultFrameRateCompatibilityChanged = 0x00400000,
eDataspaceChanged = 0x00800000,
eHdrMetadataChanged = 0x01000000,
eSurfaceDamageRegionChanged = 0x02000000,
@@ -275,6 +276,9 @@
int8_t frameRateCompatibility;
int8_t changeFrameRateStrategy;
+ // Default frame rate compatibility used to set the layer refresh rate votetype.
+ int8_t defaultFrameRateCompatibility;
+
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
// the graphic producers should receive a transform hint as if the
@@ -359,7 +363,9 @@
struct InputWindowCommands {
std::vector<gui::FocusRequest> focusRequests;
- bool syncInputWindows{false};
+ std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+ SpHash<gui::IWindowInfosReportedListener>>
+ windowInfosReportedListeners;
// Merges the passed in commands and returns true if there were any changes.
bool merge(const InputWindowCommands& other);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 9f036a6..24399ff 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -387,6 +387,22 @@
std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
};
+ // TODO(b/222421815) this class should be removed when
+ // SurfaceComposerClient::Transaction::syncInputWindows is removed and replaced with a method
+ // for adding callbacks to InputWindowCommands.
+ class Event {
+ private:
+ static constexpr std::chrono::seconds sTimeout{5};
+
+ bool mComplete = false;
+ std::condition_variable mConditionVariable;
+ std::mutex mMutex;
+
+ public:
+ void set();
+ bool wait();
+ };
+
class Transaction : public Parcelable {
private:
void releaseBufferIfOverwriting(const layer_state_t& state);
@@ -436,6 +452,8 @@
InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
+ std::shared_ptr<Event> mWindowInfosReportedEvent = nullptr;
+
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
DisplayState& getDisplayState(const sp<IBinder>& token);
@@ -583,6 +601,9 @@
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy);
+ Transaction& setDefaultFrameRateCompatibility(const sp<SurfaceControl>& sc,
+ int8_t compatibility);
+
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
// the graphic producers should receive a transform hint as if the
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 3a9b2b8..3e563b2 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -162,6 +162,10 @@
ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
}
+ void mergeWithNextTransaction(Transaction* merge, uint64_t frameNumber) {
+ mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber);
+ }
+
private:
sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
@@ -1113,6 +1117,40 @@
ASSERT_TRUE(receivedCallback);
}
+TEST_F(BLASTBufferQueueTest, SyncNextTransactionDropBuffer) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction sync;
+ adapter.setSyncTransaction(sync);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ // Merge a transaction that has a complete callback into the next frame so we can get notified
+ // when to take a screenshot
+ CallbackHelper transactionCallback;
+ Transaction t;
+ t.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext());
+ adapter.mergeWithNextTransaction(&t, 2);
+ queueBuffer(igbProducer, r, g, b, 0);
+
+ // Drop the buffer, but ensure the next one continues to get processed.
+ sync.setBuffer(mSurfaceControl, nullptr);
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+ sync.apply();
+}
+
// This test will currently fail because the old surfacecontrol will steal the last presented buffer
// until the old surface control is destroyed. This is not necessarily a bug but to document a
// limitation with the update API and to test any changes to make the api more robust. The current
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 13ca9ec..375b684 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -64,9 +64,10 @@
}
bool shouldDisregardTransformation(uint32_t source) {
- // Do not apply any transformations to axes from joysticks or touchpads.
+ // Do not apply any transformations to axes from joysticks, touchpads, or relative mice.
return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
- isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
+ isFromSource(source, AINPUT_SOURCE_CLASS_POSITION) ||
+ isFromSource(source, AINPUT_SOURCE_MOUSE_RELATIVE);
}
bool shouldDisregardOffset(uint32_t source) {
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 7f427f2..76aaf61 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -27,6 +27,8 @@
#include <utils/BitSet.h>
#include <utils/Timers.h>
+using std::literals::chrono_literals::operator""ms;
+
namespace android {
/**
@@ -57,8 +59,14 @@
// Some input devices do not send ACTION_MOVE events in the case where a pointer has
// stopped. We need to detect this case so that we can accurately predict the
// velocity after the pointer starts moving again.
-static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS;
+static const std::chrono::duration ASSUME_POINTER_STOPPED_TIME = 40ms;
+static std::string toString(std::chrono::nanoseconds t) {
+ std::stringstream stream;
+ stream.precision(1);
+ stream << std::fixed << std::chrono::duration<float, std::milli>(t).count() << " ms";
+ return stream.str();
+}
static float vectorDot(const float* a, const float* b, uint32_t m) {
float r = 0;
@@ -146,18 +154,14 @@
VelocityTracker::Strategy strategy) {
switch (strategy) {
case VelocityTracker::Strategy::IMPULSE:
- if (DEBUG_STRATEGY) {
- ALOGI("Initializing impulse strategy");
- }
+ ALOGI_IF(DEBUG_STRATEGY, "Initializing impulse strategy");
return std::make_unique<ImpulseVelocityTrackerStrategy>();
case VelocityTracker::Strategy::LSQ1:
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
case VelocityTracker::Strategy::LSQ2:
- if (DEBUG_STRATEGY && !DEBUG_IMPULSE) {
- ALOGI("Initializing lsq2 strategy");
- }
+ ALOGI_IF(DEBUG_STRATEGY && !DEBUG_IMPULSE, "Initializing lsq2 strategy");
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
case VelocityTracker::Strategy::LSQ3:
@@ -221,12 +225,11 @@
idBits.clearLastMarkedBit();
}
- if ((mCurrentPointerIdBits.value & idBits.value)
- && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
- if (DEBUG_VELOCITY) {
- ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
- (eventTime - mLastEventTime) * 0.000001f);
- }
+ if ((mCurrentPointerIdBits.value & idBits.value) &&
+ std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) {
+ ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.",
+ toString(std::chrono::nanoseconds(eventTime - mLastEventTime)).c_str());
+
// We have not received any movements for too long. Assume that all pointers
// have stopped.
mStrategy->clear();
@@ -281,8 +284,18 @@
case AMOTION_EVENT_ACTION_MOVE:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
break;
- default:
- // Ignore all other actions because they do not convey any new information about
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ case AMOTION_EVENT_ACTION_UP: {
+ std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime);
+ if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) {
+ ALOGD_IF(DEBUG_VELOCITY,
+ "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.",
+ toString(delaySinceLastEvent).c_str());
+ // We have not received any movements for too long. Assume that all pointers
+ // have stopped.
+ mStrategy->clear();
+ }
+ // These actions because they do not convey any new information about
// pointer movement. We also want to preserve the last known velocity of the pointers.
// Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
// of the pointers that went up. ACTION_POINTER_UP does include the new position of
@@ -292,6 +305,10 @@
// before adding the movement.
return;
}
+ default:
+ // Ignore all other actions.
+ return;
+ }
size_t pointerCount = event->getPointerCount();
if (pointerCount > MAX_POINTERS) {
@@ -438,10 +455,10 @@
static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
const size_t m = x.size();
- if (DEBUG_STRATEGY) {
- ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
- vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str());
- }
+
+ ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
+ vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str());
+
LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
// Expand the X vector to a matrix A, pre-multiplied by the weights.
@@ -452,9 +469,9 @@
a[i][h] = a[i - 1][h] * x[h];
}
}
- if (DEBUG_STRATEGY) {
- ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
- }
+
+ ALOGD_IF(DEBUG_STRATEGY, " - a=%s",
+ matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
float q[n][m]; // orthonormal basis, column-major order
@@ -473,9 +490,7 @@
float norm = vectorNorm(&q[j][0], m);
if (norm < 0.000001f) {
// vectors are linearly dependent or zero so no solution
- if (DEBUG_STRATEGY) {
- ALOGD(" - no solution, norm=%f", norm);
- }
+ ALOGD_IF(DEBUG_STRATEGY, " - no solution, norm=%f", norm);
return false;
}
@@ -518,9 +533,8 @@
}
outB[i] /= r[i][i];
}
- if (DEBUG_STRATEGY) {
- ALOGD(" - b=%s", vectorToString(outB, n).c_str());
- }
+
+ ALOGD_IF(DEBUG_STRATEGY, " - b=%s", vectorToString(outB, n).c_str());
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
// SSerr is the residual sum of squares (variance of the error),
@@ -546,11 +560,11 @@
sstot += w[h] * w[h] * var * var;
}
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
- if (DEBUG_STRATEGY) {
- ALOGD(" - sserr=%f", sserr);
- ALOGD(" - sstot=%f", sstot);
- ALOGD(" - det=%f", *outDet);
- }
+
+ ALOGD_IF(DEBUG_STRATEGY, " - sserr=%f", sserr);
+ ALOGD_IF(DEBUG_STRATEGY, " - sstot=%f", sstot);
+ ALOGD_IF(DEBUG_STRATEGY, " - det=%f", *outDet);
+
return true;
}
@@ -673,11 +687,11 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
- if (DEBUG_STRATEGY) {
- ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
- int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
- vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
- }
+
+ ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+ int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
+ vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
+
return true;
}
}
@@ -1185,9 +1199,10 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = 2; // similar results to 2nd degree fit
outEstimator->confidence = 1;
- if (DEBUG_STRATEGY) {
- ALOGD("velocity: (%.1f, %.1f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
- }
+
+ ALOGD_IF(DEBUG_STRATEGY, "velocity: (%.1f, %.1f)", outEstimator->xCoeff[1],
+ outEstimator->yCoeff[1]);
+
if (DEBUG_IMPULSE) {
// TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
// Calculate the lsq2 velocity for the same inputs to allow runtime comparisons
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index a92016b..4b31246 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -715,10 +715,10 @@
}
TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
- constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
- AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_JOYSTICK,
- AMOTION_EVENT_ACTION_MOVE)};
+ constexpr static std::array kNonTransformedSources =
+ {std::pair(AINPUT_SOURCE_TOUCHPAD, AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_JOYSTICK, AMOTION_EVENT_ACTION_MOVE),
+ std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, AMOTION_EVENT_ACTION_MOVE)};
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform transform(ui::Transform::ROT_90, 800, 400);
transform.set(transform.tx() + 20, transform.ty() + 40);
@@ -738,7 +738,7 @@
TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+ std::pair(AINPUT_SOURCE_TOUCH_NAVIGATION,
AMOTION_EVENT_ACTION_MOVE)};
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform transform(ui::Transform::ROT_90, 800, 400);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index a87b187..4a445de 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -26,7 +26,9 @@
#include <gui/constants.h>
#include <input/VelocityTracker.h>
-using namespace std::chrono_literals;
+using std::literals::chrono_literals::operator""ms;
+using std::literals::chrono_literals::operator""ns;
+using std::literals::chrono_literals::operator""us;
using android::base::StringPrintf;
namespace android {
@@ -149,8 +151,7 @@
if (i == 0) {
action = AMOTION_EVENT_ACTION_DOWN;
EXPECT_EQ(1U, pointerCount) << "First event should only have 1 pointer";
- } else if (i == motions.size() - 1) {
- EXPECT_EQ(1U, pointerCount) << "Last event should only have 1 pointer";
+ } else if ((i == motions.size() - 1) && pointerCount == 1) {
action = AMOTION_EVENT_ACTION_UP;
} else {
const MotionEventEntry& previousEntry = motions[i-1];
@@ -195,7 +196,7 @@
static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
const std::vector<MotionEventEntry>& motions, int32_t axis,
- float targetVelocity) {
+ float targetVelocity, uint32_t pointerId = DEFAULT_POINTER_ID) {
VelocityTracker vt(strategy);
float Vx, Vy;
@@ -204,7 +205,7 @@
vt.addMovement(&event);
}
- vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy);
+ vt.getVelocity(pointerId, &Vx, &Vy);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
@@ -846,13 +847,71 @@
// Velocity should actually be zero, but we expect 0.016 here instead.
// This is close enough to zero, and is likely caused by division by a very small number.
- computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016);
- computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, 0);
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
}
/**
+ * ================= Pointer liftoff ===============================================================
+ */
+
+/**
+ * The last movement of a pointer is always ACTION_POINTER_UP or ACTION_UP. If there's a short delay
+ * between the last ACTION_MOVE and the next ACTION_POINTER_UP or ACTION_UP, velocity should not be
+ * affected by the liftoff.
+ */
+TEST_F(VelocityTrackerTest, ShortDelayBeforeActionUp) {
+ std::vector<MotionEventEntry> motions = {
+ {0ms, {{10, 0}}}, {10ms, {{20, 0}}}, {20ms, {{30, 0}}}, {30ms, {{30, 0}}}, // ACTION_UP
+ };
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1000);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 1000);
+}
+
+/**
+ * The last movement of a single pointer is ACTION_UP. If there's a long delay between the last
+ * ACTION_MOVE and the final ACTION_UP, velocity should be reported as zero because the pointer
+ * should be assumed to have stopped.
+ */
+TEST_F(VelocityTrackerTest, LongDelayBeforeActionUp) {
+ std::vector<MotionEventEntry> motions = {
+ {0ms, {{10, 0}}},
+ {10ms, {{20, 0}}},
+ {20ms, {{30, 0}}},
+ {3000ms, {{30, 0}}}, // ACTION_UP
+ };
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
+}
+
+/**
+ * The last movement of a pointer is always ACTION_POINTER_UP or ACTION_UP. If there's a long delay
+ * before ACTION_POINTER_UP event, the movement should be assumed to have stopped.
+ * The final velocity should be reported as zero for all pointers.
+ */
+TEST_F(VelocityTrackerTest, LongDelayBeforeActionPointerUp) {
+ std::vector<MotionEventEntry> motions = {
+ {0ms, {{10, 0}}},
+ {10ms, {{20, 0}, {100, 0}}},
+ {20ms, {{30, 0}, {200, 0}}},
+ {30ms, {{30, 0}, {300, 0}}},
+ {40ms, {{30, 0}, {400, 0}}},
+ {3000ms, {{30, 0}}}, // ACTION_POINTER_UP
+ };
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0,
+ /*pointerId*/ 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0,
+ /*pointerId*/ 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0,
+ /*pointerId*/ 1);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0,
+ /*pointerId*/ 1);
+}
+
+/**
* ================== Tests for least squares fitting ==============================================
*
* Special care must be taken when constructing tests for LeastSquaresVelocityTrackerStrategy
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index ec11b81..731f989 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -193,6 +193,13 @@
return query(window, NATIVE_WINDOW_DATASPACE);
}
+int32_t ANativeWindow_getBuffersDefaultDataSpace(ANativeWindow* window) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return -EINVAL;
+ }
+ return query(window, NATIVE_WINDOW_DEFAULT_DATASPACE);
+}
+
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
return ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index f0e1c4d..20f4b52 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -227,6 +227,16 @@
*/
int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN(28);
+/**
+ * Get the default dataspace of the buffers in window as set by the consumer.
+ *
+ * Available since API level 34.
+ *
+ * \return the dataspace of buffers in window, ADATASPACE_UNKNOWN is returned if
+ * dataspace is unknown, or -EINVAL if window is invalid.
+ */
+int32_t ANativeWindow_getBuffersDefaultDataSpace(ANativeWindow* window) __INTRODUCED_IN(34);
+
/** Compatibility value for ANativeWindow_setFrameRate. */
enum ANativeWindow_FrameRateCompatibility {
/**
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index a54af1f..79f49e1 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1034,6 +1034,11 @@
* This surface is ignored while choosing the refresh rate.
*/
ANATIVEWINDOW_FRAME_RATE_NO_VOTE,
+
+ /**
+ * This surface will vote for the minimum refresh rate.
+ */
+ ANATIVEWINDOW_FRAME_RATE_MIN
};
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 988132c..f8c9e4a 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -21,6 +21,7 @@
ANativeWindow_cancelBuffer; # llndk
ANativeWindow_dequeueBuffer; # llndk
ANativeWindow_getBuffersDataSpace; # introduced=28
+ ANativeWindow_getBuffersDefaultDataSpace; # introduced=34
ANativeWindow_getFormat;
ANativeWindow_getHeight;
ANativeWindow_getLastDequeueDuration; # apex # introduced=30
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index c9f9ec3..5187d7b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -779,15 +779,9 @@
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
base::unique_fd&& bufferFence) {
- ATRACE_NAME("SkiaGL::drawLayers");
+ ATRACE_NAME("SkiaGL::drawLayersInternal");
std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (layers.empty()) {
- ALOGV("Drawing empty layer stack");
- resultPromise->set_value({NO_ERROR, base::unique_fd()});
- return;
- }
-
if (buffer == nullptr) {
ALOGE("No output buffer provided. Aborting GPU composition.");
resultPromise->set_value({BAD_VALUE, base::unique_fd()});
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 61af698..3340857 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -1606,6 +1606,36 @@
drawEmptyLayers();
}
+TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) {
+ const auto& renderEngineFactory = GetParam();
+ if (renderEngineFactory->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ // add a red layer
+ renderengine::LayerSettings layerOne{
+ .geometry.boundaries = fullscreenRect().toFloatRect(),
+ .source.solidColor = half3(1.0f, 0.0f, 0.0f),
+ .alpha = 1.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layersFirst{layerOne};
+ invokeDraw(settings, layersFirst);
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+
+ // re-draw with an empty layer above it, and we get a transparent black one
+ std::vector<renderengine::LayerSettings> layersSecond;
+ invokeDraw(settings, layersSecond);
+ expectBufferColor(fullscreenRect(), 0, 0, 0, 0);
+}
+
TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
initializeRenderEngine();
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index f4dbe49..7619a50 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -1453,7 +1453,11 @@
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ if (err != 0) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ } else if (!s->cnx->useAngle) {
+ return EGL_TRUE;
+ } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
if (attribute == EGL_TIMESTAMPS_ANDROID) {
@@ -1463,7 +1467,11 @@
return EGL_TRUE;
}
int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ if (err != 0) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ } else if (!s->cnx->useAngle) {
+ return EGL_TRUE;
+ } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
if (s->setSmpte2086Attribute(attribute, value)) {
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 3c7644f..7ea2288 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -76,6 +76,12 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ // This is required for test due to GpuStats instance spawns binder transactions
+ // in its destructor. After the gtest destructor test exits immidiatelly.
+ // It results in binder thread not able to process above binder transactions and memory leak
+ // occures. Binder thread needs time to process callbacks transactions.
+ // It leads to GpuStats instance destructor needs to be called in advance.
+ mGpuStats.reset(nullptr);
// performs all pending callbacks until all data has been consumed
// gives time to process binder transactions by thread pool
looper->pollAll(1000);
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 8ce2f35..21695c3 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -331,20 +331,9 @@
enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
}
-const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
- if (!mService) {
- return "null";
- }
-
- if (AIBinder_ping(mService->asBinder().get()) == STATUS_OK) {
- return "running";
- }
- return "not responding";
-}
-
void MotionClassifier::dump(std::string& dump) {
std::scoped_lock lock(mLock);
- dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
+ dump += StringPrintf(INDENT2 "mService connected: %s\n", mService ? "true" : "false");
dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
mEvents.size(), MAX_EVENTS);
dump += INDENT2 "mClassifications, mLastDownTimes:\n";
@@ -365,6 +354,18 @@
}
}
+void MotionClassifier::monitor() {
+ std::scoped_lock lock(mLock);
+ if (mService) {
+ // Ping the HAL service to ensure it is alive and not blocked.
+ const binder_status_t status = AIBinder_ping(mService->asBinder().get());
+ if (status != STATUS_OK) {
+ ALOGW("IInputProcessor HAL is not responding; binder ping result: %s",
+ AStatus_getDescription(AStatus_fromStatus(status)));
+ }
+ }
+}
+
// --- InputClassifier ---
InputClassifier::InputClassifier(InputListenerInterface& listener) : mQueuedListener(listener) {}
@@ -504,6 +505,7 @@
void InputClassifier::monitor() {
std::scoped_lock lock(mLock);
+ if (mMotionClassifier) mMotionClassifier->monitor();
}
InputClassifier::~InputClassifier() {
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 56cf760..9b31a3c 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -78,9 +78,14 @@
virtual void reset(const NotifyDeviceResetArgs& args) = 0;
/**
- * Dump the state of the motion classifier
+ * Dump the state of the motion classifier.
*/
virtual void dump(std::string& dump) = 0;
+
+ /**
+ * Called by the heartbeat to ensure the HAL is still processing normally.
+ */
+ virtual void monitor() = 0;
};
/**
@@ -96,7 +101,7 @@
*/
virtual void dump(std::string& dump) = 0;
- /* Called by the heatbeat to ensures that the classifier has not deadlocked. */
+ /** Called by the heartbeat to ensure that the classifier has not deadlocked. */
virtual void monitor() = 0;
InputClassifierInterface() { }
@@ -155,13 +160,14 @@
virtual void reset(const NotifyDeviceResetArgs& args) override;
virtual void dump(std::string& dump) override;
+ virtual void monitor() override;
private:
friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
explicit MotionClassifier(
std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> service);
- // The events that need to be sent to the HAL.
+ /** The events that need to be sent to the HAL. */
BlockingQueue<ClassifierEvent> mEvents;
/**
* Add an event to the queue mEvents.
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
index 82c6ee1..c88bfe9 100644
--- a/services/inputflinger/OWNERS
+++ b/services/inputflinger/OWNERS
@@ -1,3 +1 @@
-lzye@google.com
-michaelwr@google.com
-svv@google.com
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 318940b..f673a28 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -45,6 +45,14 @@
]
},
{
+ "name": "CtsWidgetTestCases",
+ "options": [
+ {
+ "include-filter": "android.widget.cts.NumberPickerTest"
+ }
+ ]
+ },
+ {
"name": "FrameworksCoreTests",
"options": [
{
@@ -124,6 +132,14 @@
]
},
{
+ "name": "CtsWidgetTestCases",
+ "options": [
+ {
+ "include-filter": "android.widget.cts.NumberPickerTest"
+ }
+ ]
+ },
+ {
"name": "FrameworksCoreTests",
"options": [
{
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5e92f0d..625c367 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1872,6 +1872,17 @@
return false;
}
+static std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
+ if (eventEntry.type == EventEntry::Type::KEY) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+ return keyEntry.downTime;
+ } else if (eventEntry.type == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+ return motionEntry.downTime;
+ }
+ return std::nullopt;
+}
+
InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime) {
@@ -1961,7 +1972,7 @@
// Success! Output targets.
addWindowTargetLocked(focusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0), inputTargets);
+ BitSet32(0), getDownTime(entry), inputTargets);
// Done.
return InputEventInjectionResult::SUCCEEDED;
@@ -2200,7 +2211,8 @@
pointerIds.markBit(pointerId);
}
- tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
+ entry.eventTime);
}
// If any existing window is pilfering pointers from newly added window, remove it
@@ -2287,7 +2299,8 @@
if (isSplit) {
pointerIds.markBit(entry.pointerProperties[0].id);
}
- tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds,
+ entry.eventTime);
}
}
}
@@ -2404,7 +2417,7 @@
InputTarget::
FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0));
+ BitSet32(0), entry.eventTime);
}
}
}
@@ -2415,7 +2428,8 @@
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, inputTargets);
+ touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+ inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
@@ -2600,6 +2614,7 @@
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) {
std::vector<InputTarget>::iterator it =
std::find_if(inputTargets.begin(), inputTargets.end(),
@@ -2621,6 +2636,7 @@
inputTarget.inputChannel = inputChannel;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+ inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId);
if (displayInfoIt != mDisplayInfos.end()) {
inputTarget.displayTransform = displayInfoIt->second.transform;
@@ -2646,6 +2662,8 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ // target.firstDownTimeInTarget is not set for global monitors. It is only required in split
+ // touch and global monitoring works as intended even without setting firstDownTimeInTarget
if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
target.displayTransform = it->second.transform;
}
@@ -2930,8 +2948,12 @@
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
+ LOG_ALWAYS_FATAL_IF(!inputTarget.firstDownTimeInTarget.has_value(),
+ "Splitting motion events requires a down time to be set for the "
+ "target");
std::unique_ptr<MotionEntry> splitMotionEntry =
- splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
+ splitMotionEvent(originalMotionEntry, inputTarget.pointerIds,
+ inputTarget.firstDownTimeInTarget.value());
if (!splitMotionEntry) {
return; // split event was dropped
}
@@ -3687,15 +3709,13 @@
}
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
- const sp<Connection>& connection) {
+ const nsecs_t downTime, const sp<Connection>& connection) {
if (connection->status == Connection::Status::BROKEN) {
return;
}
- nsecs_t currentTime = now();
-
std::vector<std::unique_ptr<EventEntry>> downEvents =
- connection->inputState.synthesizePointerDownEvents(currentTime);
+ connection->inputState.synthesizePointerDownEvents(downTime);
if (downEvents.empty()) {
return;
@@ -3743,11 +3763,11 @@
InputTarget::FLAG_DISPATCH_AS_IS);
}
- startDispatchCycleLocked(currentTime, connection);
+ startDispatchCycleLocked(downTime, connection);
}
std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
- const MotionEntry& originalMotionEntry, BitSet32 pointerIds) {
+ const MotionEntry& originalMotionEntry, BitSet32 pointerIds, nsecs_t splitDownTime) {
ALOG_ASSERT(pointerIds.value != 0);
uint32_t splitPointerIndexMap[MAX_POINTERS];
@@ -3815,6 +3835,13 @@
}
}
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ LOG_ALWAYS_FATAL_IF(splitDownTime != originalMotionEntry.eventTime,
+ "Split motion event has mismatching downTime and eventTime for "
+ "ACTION_DOWN, motionEntry=%s, splitDownTime=%" PRId64 "ms",
+ originalMotionEntry.getDescription().c_str(), ns2ms(splitDownTime));
+ }
+
int32_t newId = mIdGenerator.nextId();
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("Split MotionEvent(id=0x%" PRIx32
@@ -3835,9 +3862,9 @@
originalMotionEntry.xPrecision,
originalMotionEntry.yPrecision,
originalMotionEntry.xCursorPosition,
- originalMotionEntry.yCursorPosition,
- originalMotionEntry.downTime, splitPointerCount,
- splitPointerProperties, splitPointerCoords);
+ originalMotionEntry.yCursorPosition, splitDownTime,
+ splitPointerCount, splitPointerProperties,
+ splitPointerCoords);
if (originalMotionEntry.injectionState) {
splitMotionEntry->injectionState = originalMotionEntry.injectionState;
@@ -5074,12 +5101,13 @@
state->removeWindowByToken(fromToken);
// Add new window.
+ nsecs_t downTimeInTarget = now();
int32_t newTargetFlags =
oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
newTargetFlags |= InputTarget::FLAG_FOREGROUND;
}
- state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
+ state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds, downTimeInTarget);
// Store the dragging window.
if (isDragDrop) {
@@ -5103,7 +5131,7 @@
options(CancelationOptions::CANCEL_POINTER_EVENTS,
"transferring touch focus from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
- synthesizePointerDownEventsForConnectionLocked(toConnection);
+ synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection);
}
if (DEBUG_FOCUS) {
@@ -5253,10 +5281,12 @@
dump += INDENT3 "Windows:\n";
for (size_t i = 0; i < state.windows.size(); i++) {
const TouchedWindow& touchedWindow = state.windows[i];
- dump += StringPrintf(INDENT4
- "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+ dump += StringPrintf(INDENT4 "%zu: name='%s', pointerIds=0x%0x, "
+ "targetFlags=0x%x, firstDownTimeInTarget=%" PRId64
+ "ms\n",
i, touchedWindow.windowHandle->getName().c_str(),
- touchedWindow.pointerIds.value, touchedWindow.targetFlags);
+ touchedWindow.pointerIds.value, touchedWindow.targetFlags,
+ ns2ms(touchedWindow.firstDownTimeInTarget.value_or(0)));
}
} else {
dump += INDENT3 "Windows: <none>\n";
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 50124a6..da4af48 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -550,6 +550,7 @@
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
REQUIRES(mLock);
@@ -621,12 +622,14 @@
const CancelationOptions& options)
REQUIRES(mLock);
- void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection)
+ void synthesizePointerDownEventsForConnectionLocked(const nsecs_t downTime,
+ const sp<Connection>& connection)
REQUIRES(mLock);
- // Splitting motion events across windows.
+ // Splitting motion events across windows. When splitting motion event for a target,
+ // splitDownTime refers to the time of first 'down' event on that particular target
std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
- BitSet32 pointerIds);
+ BitSet32 pointerIds, nsecs_t splitDownTime);
// Reset and drop everything the dispatcher is doing.
void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 0725389..ac20dab 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -106,6 +106,9 @@
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
+ // Event time for the first motion event (ACTION_DOWN) dispatched to this input target if
+ // FLAG_SPLIT is set.
+ std::optional<nsecs_t> firstDownTimeInTarget;
// The data is stored by the pointerId. Use the bit position of pointerIds to look up
// Transform per pointerId.
ui::Transform pointerTransforms[MAX_POINTERS];
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 51c6826..cf0c38a 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -30,7 +30,7 @@
}
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags,
- BitSet32 pointerIds) {
+ BitSet32 pointerIds, std::optional<nsecs_t> eventTime) {
if (targetFlags & InputTarget::FLAG_SPLIT) {
split = true;
}
@@ -42,7 +42,13 @@
if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
}
+ // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
+ // downTime set initially. Need to update existing window when an pointer is down for
+ // the window.
touchedWindow.pointerIds.value |= pointerIds.value;
+ if (!touchedWindow.firstDownTimeInTarget.has_value()) {
+ touchedWindow.firstDownTimeInTarget = eventTime;
+ }
return;
}
}
@@ -51,6 +57,7 @@
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
touchedWindow.pointerIds = pointerIds;
+ touchedWindow.firstDownTimeInTarget = eventTime;
windows.push_back(touchedWindow);
}
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 327863f..1fb51e1 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -47,7 +47,8 @@
void reset();
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds);
+ int32_t targetFlags, BitSet32 pointerIds,
+ std::optional<nsecs_t> eventTime = std::nullopt);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
void filterWindowsExcept(const sp<IBinder>& token);
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 83b52a4..0962d0c 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -31,6 +31,9 @@
int32_t targetFlags;
BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
bool isPilferingPointers = false;
+ // Time at which the first action down occurred on this window.
+ // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
+ std::optional<nsecs_t> firstDownTimeInTarget;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3bd3275..01146a3 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,7 +71,7 @@
"libstatslog",
"libui",
"libutils",
- "PlatformProperties",
+ "libPlatformProperties",
],
static_libs: [
"libc++fs",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 6a8ed49..fe4d443 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -687,6 +687,7 @@
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
mINotifyFd = inotify_init1(IN_CLOEXEC);
+ LOG_ALWAYS_FATAL_IF(mINotifyFd < 0, "Could not create inotify instance: %s", strerror(errno));
std::error_code errorCode;
bool isDeviceInotifyAdded = false;
@@ -1723,7 +1724,10 @@
// before closing the devices.
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
- readNotifyLocked();
+ const auto res = readNotifyLocked();
+ if (!res.ok()) {
+ ALOGW("Failed to read from inotify: %s", res.error().message().c_str());
+ }
deviceChanged = true;
}
@@ -2413,53 +2417,56 @@
mDevices.erase(device.id);
}
-status_t EventHub::readNotifyLocked() {
- int res;
- char event_buf[512];
- int event_size;
- int event_pos = 0;
- struct inotify_event* event;
+base::Result<void> EventHub::readNotifyLocked() {
+ static constexpr auto EVENT_SIZE = static_cast<ssize_t>(sizeof(inotify_event));
+ uint8_t eventBuffer[512];
+ ssize_t sizeRead;
ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
- res = read(mINotifyFd, event_buf, sizeof(event_buf));
- if (res < (int)sizeof(*event)) {
- if (errno == EINTR) return 0;
- ALOGW("could not get event, %s\n", strerror(errno));
- return -1;
- }
+ do {
+ sizeRead = read(mINotifyFd, eventBuffer, sizeof(eventBuffer));
+ } while (sizeRead < 0 && errno == EINTR);
- while (res >= (int)sizeof(*event)) {
- event = (struct inotify_event*)(event_buf + event_pos);
- if (event->len) {
- if (event->wd == mDeviceInputWd) {
- std::string filename = std::string(DEVICE_INPUT_PATH) + "/" + event->name;
- if (event->mask & IN_CREATE) {
- openDeviceLocked(filename);
- } else {
- ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
- closeDeviceByPathLocked(filename);
- }
- } else if (event->wd == mDeviceWd) {
- if (isV4lTouchNode(event->name)) {
- std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
- if (event->mask & IN_CREATE) {
- openVideoDeviceLocked(filename);
- } else {
- ALOGI("Removing video device '%s' due to inotify event", filename.c_str());
- closeVideoDeviceByPathLocked(filename);
- }
- } else if (strcmp(event->name, "input") == 0 && event->mask & IN_CREATE) {
- addDeviceInputInotify();
- }
- } else {
- LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
- }
- }
- event_size = sizeof(*event) + event->len;
- res -= event_size;
- event_pos += event_size;
+ if (sizeRead < EVENT_SIZE) return Errorf("could not get event, %s", strerror(errno));
+
+ for (ssize_t eventPos = 0; sizeRead >= EVENT_SIZE;) {
+ const inotify_event* event;
+ event = (const inotify_event*)(eventBuffer + eventPos);
+ if (event->len == 0) continue;
+
+ handleNotifyEventLocked(*event);
+
+ const ssize_t eventSize = EVENT_SIZE + event->len;
+ sizeRead -= eventSize;
+ eventPos += eventSize;
}
- return 0;
+ return {};
+}
+
+void EventHub::handleNotifyEventLocked(const inotify_event& event) {
+ if (event.wd == mDeviceInputWd) {
+ std::string filename = std::string(DEVICE_INPUT_PATH) + "/" + event.name;
+ if (event.mask & IN_CREATE) {
+ openDeviceLocked(filename);
+ } else {
+ ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
+ closeDeviceByPathLocked(filename);
+ }
+ } else if (event.wd == mDeviceWd) {
+ if (isV4lTouchNode(event.name)) {
+ std::string filename = std::string(DEVICE_PATH) + "/" + event.name;
+ if (event.mask & IN_CREATE) {
+ openVideoDeviceLocked(filename);
+ } else {
+ ALOGI("Removing video device '%s' due to inotify event", filename.c_str());
+ closeVideoDeviceByPathLocked(filename);
+ }
+ } else if (strcmp(event.name, "input") == 0 && event.mask & IN_CREATE) {
+ addDeviceInputInotify();
+ }
+ } else {
+ LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event.wd);
+ }
}
status_t EventHub::scanDirLocked(const std::string& dirname) {
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 5453ebb..b2d50fc 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -44,6 +44,8 @@
#include "TouchVideoDevice.h"
#include "VibrationElement.h"
+struct inotify_event;
+
namespace android {
/* Number of colors : {red, green, blue} */
@@ -648,7 +650,8 @@
status_t scanDirLocked(const std::string& dirname) REQUIRES(mLock);
status_t scanVideoDirLocked(const std::string& dirname) REQUIRES(mLock);
void scanDevicesLocked() REQUIRES(mLock);
- status_t readNotifyLocked() REQUIRES(mLock);
+ base::Result<void> readNotifyLocked() REQUIRES(mLock);
+ void handleNotifyEventLocked(const inotify_event&) REQUIRES(mLock);
Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock);
Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index a9a4c71..91dc619 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -76,7 +76,7 @@
void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- if (mParameters.mode == Parameters::MODE_POINTER) {
+ if (mParameters.mode == Parameters::Mode::POINTER) {
float minX, minY, maxX, maxY;
if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
@@ -131,12 +131,12 @@
// Configure device mode.
switch (mParameters.mode) {
- case Parameters::MODE_POINTER_RELATIVE:
+ case Parameters::Mode::POINTER_RELATIVE:
// Should not happen during first time configuration.
ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
- mParameters.mode = Parameters::MODE_POINTER;
+ mParameters.mode = Parameters::Mode::POINTER;
[[fallthrough]];
- case Parameters::MODE_POINTER:
+ case Parameters::Mode::POINTER:
mSource = AINPUT_SOURCE_MOUSE;
mXPrecision = 1.0f;
mYPrecision = 1.0f;
@@ -144,7 +144,7 @@
mYScale = 1.0f;
mPointerController = getContext()->getPointerController(getDeviceId());
break;
- case Parameters::MODE_NAVIGATION:
+ case Parameters::Mode::NAVIGATION:
mSource = AINPUT_SOURCE_TRACKBALL;
mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
@@ -157,12 +157,13 @@
mHWheelScale = 1.0f;
}
- const bool configurePointerCapture = (!changes && config->pointerCaptureRequest.enable) ||
- (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
+ ((!changes && config->pointerCaptureRequest.enable) ||
+ (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE));
if (configurePointerCapture) {
if (config->pointerCaptureRequest.enable) {
- if (mParameters.mode == Parameters::MODE_POINTER) {
- mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
+ if (mParameters.mode == Parameters::Mode::POINTER) {
+ mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
// Keep PointerController around in order to preserve the pointer position.
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -170,8 +171,8 @@
ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
}
} else {
- if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
- mParameters.mode = Parameters::MODE_POINTER;
+ if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+ mParameters.mode = Parameters::Mode::POINTER;
mSource = AINPUT_SOURCE_MOUSE;
} else {
ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
@@ -186,8 +187,8 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) ||
configurePointerCapture) {
- if (config->pointerCaptureRequest.enable) {
- // Disable any acceleration or scaling when Pointer Capture is enabled.
+ if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+ // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
@@ -198,7 +199,8 @@
}
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
+ configurePointerCapture) {
mOrientation = DISPLAY_ORIENTATION_0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
@@ -207,8 +209,9 @@
// anything if the device is already orientation-aware. If the device is not
// orientation-aware, then we need to apply the inverse rotation of the display so that
// when the display rotation is applied later as a part of the per-window transform, we
- // get the expected screen coordinates.
- if (!isOrientedDevice) {
+ // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
+ // rotations and report values directly from the input device.
+ if (!isOrientedDevice && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
std::optional<DisplayViewport> internalViewport =
config->getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
@@ -221,12 +224,12 @@
}
void CursorInputMapper::configureParameters() {
- mParameters.mode = Parameters::MODE_POINTER;
+ mParameters.mode = Parameters::Mode::POINTER;
String8 cursorModeString;
if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
cursorModeString)) {
if (cursorModeString == "navigation") {
- mParameters.mode = Parameters::MODE_NAVIGATION;
+ mParameters.mode = Parameters::Mode::NAVIGATION;
} else if (cursorModeString != "pointer" && cursorModeString != "default") {
ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
}
@@ -237,7 +240,7 @@
mParameters.orientationAware);
mParameters.hasAssociatedDisplay = false;
- if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
+ if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
mParameters.hasAssociatedDisplay = true;
}
}
@@ -246,21 +249,7 @@
dump += INDENT3 "Parameters:\n";
dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
toString(mParameters.hasAssociatedDisplay));
-
- switch (mParameters.mode) {
- case Parameters::MODE_POINTER:
- dump += INDENT4 "Mode: pointer\n";
- break;
- case Parameters::MODE_POINTER_RELATIVE:
- dump += INDENT4 "Mode: relative pointer\n";
- break;
- case Parameters::MODE_NAVIGATION:
- dump += INDENT4 "Mode: navigation\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
+ dump += StringPrintf(INDENT4 "Mode: %s\n", ftl::enum_string(mParameters.mode).c_str());
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
}
@@ -486,7 +475,7 @@
std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
if (mParameters.hasAssociatedDisplay) {
- if (mParameters.mode == Parameters::MODE_POINTER) {
+ if (mParameters.mode == Parameters::Mode::POINTER) {
return std::make_optional(mPointerController->getDisplayId());
} else {
// If the device is orientationAware and not a mouse,
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index c84c6c4..75aeffb 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -74,10 +74,17 @@
// Immutable configuration parameters.
struct Parameters {
- enum Mode {
- MODE_POINTER,
- MODE_POINTER_RELATIVE,
- MODE_NAVIGATION,
+ enum class Mode {
+ // In POINTER mode, the device is a mouse that controls the mouse cursor on the screen,
+ // reporting absolute screen locations using SOURCE_MOUSE.
+ POINTER,
+ // A mouse device in POINTER mode switches to the POINTER_RELATIVE mode when Pointer
+ // Capture is enabled, and reports relative values only using SOURCE_MOUSE_RELATIVE.
+ POINTER_RELATIVE,
+ // A device in NAVIGATION mode emits relative values using SOURCE_TRACKBALL.
+ NAVIGATION,
+
+ ftl_last = NAVIGATION,
};
Mode mode;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index d6b72ed..f07a3e8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1053,6 +1053,10 @@
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
} else {
+ if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT &&
+ !mConfig.showTouches) {
+ mPointerController->clearSpots();
+ }
mPointerController.reset();
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 8af7cc3..70fd25c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -58,6 +58,8 @@
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_2_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_3_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
@@ -1941,6 +1943,77 @@
window2->consumeMotionDown();
}
+/**
+ * When splitting touch events the downTime should be adjusted such that the downTime corresponds
+ * to the event time of the first ACTION_DOWN sent to the particular window.
+ */
+TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window1 =
+ new FakeWindowHandle(application, mDispatcher, "Window1", DISPLAY_ID);
+ window1->setTouchableRegion(Region{{0, 0, 100, 100}});
+ sp<FakeWindowHandle> window2 =
+ new FakeWindowHandle(application, mDispatcher, "Window2", DISPLAY_ID);
+ window2->setTouchableRegion(Region{{100, 0, 200, 100}});
+
+ mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
+
+ NotifyMotionArgs args;
+ // Touch down on the first window
+ mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}})));
+
+ mDispatcher->waitForIdle();
+ InputEvent* inputEvent1 = window1->consume();
+ window2->assertNoEvents();
+ MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
+ nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
+ ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
+
+ // Now touch down on the window with another pointer
+ mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
+ mDispatcher->waitForIdle();
+ InputEvent* inputEvent2 = window2->consume();
+ MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
+ nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
+ ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
+ ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
+
+ // Now move the pointer on the second window
+ mDispatcher->notifyMotion(
+ &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})));
+ mDispatcher->waitForIdle();
+ InputEvent* inputEvent3 = window2->consume();
+ MotionEvent& motionEvent3 = static_cast<MotionEvent&>(*inputEvent3);
+ ASSERT_EQ(motionEvent3.getDownTime(), downTimeForWindow2);
+
+ // Now add new touch down on the second window
+ mDispatcher->notifyMotion(
+ &(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})));
+ mDispatcher->waitForIdle();
+ InputEvent* inputEvent4 = window2->consume();
+ MotionEvent& motionEvent4 = static_cast<MotionEvent&>(*inputEvent4);
+ ASSERT_EQ(motionEvent4.getDownTime(), downTimeForWindow2);
+
+ // TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
+ window1->consumeMotionMove();
+ window1->assertNoEvents();
+
+ // Now move the pointer on the first window
+ mDispatcher->notifyMotion(
+ &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
+ mDispatcher->waitForIdle();
+ InputEvent* inputEvent5 = window1->consume();
+ MotionEvent& motionEvent5 = static_cast<MotionEvent&>(*inputEvent5);
+ ASSERT_EQ(motionEvent5.getDownTime(), downTimeForWindow1);
+
+ mDispatcher->notifyMotion(&(
+ args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})));
+ mDispatcher->waitForIdle();
+ InputEvent* inputEvent6 = window1->consume();
+ MotionEvent& motionEvent6 = static_cast<MotionEvent&>(*inputEvent6);
+ ASSERT_EQ(motionEvent6.getDownTime(), downTimeForWindow1);
+}
+
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 1380112..901b4de 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -219,7 +219,7 @@
mSpotsByDisplay[displayId] = newSpots;
}
- void clearSpots() override {}
+ void clearSpots() override { mSpotsByDisplay.clear(); }
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
};
@@ -4971,6 +4971,48 @@
ASSERT_EQ(20, args.pointerCoords[0].getY());
}
+TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
+
+ // Ensure the display is rotated.
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+
+ NotifyMotionArgs args;
+
+ // Verify that the coordinates are rotated.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+ ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X));
+ ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+
+ // Enable Pointer Capture.
+ mFakePolicy->setPointerCapture(true);
+ configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ NotifyPointerCaptureChangedArgs captureArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
+ ASSERT_TRUE(captureArgs.request.enable);
+
+ // Move and verify rotation is not applied.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_EQ(10, args.pointerCoords[0].getX());
+ ASSERT_EQ(20, args.pointerCoords[0].getY());
+}
+
TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
@@ -8580,7 +8622,8 @@
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
// Two fingers down at default display.
int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -8607,6 +8650,13 @@
iter = fakePointerController->getSpots().find(SECONDARY_DISPLAY_ID);
ASSERT_TRUE(iter != fakePointerController->getSpots().end());
ASSERT_EQ(size_t(2), iter->second.size());
+
+ // Disable the show touches configuration and ensure the spots are cleared.
+ mFakePolicy->setShowTouches(false);
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+
+ ASSERT_TRUE(fakePointerController->getSpots().empty());
}
TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 1ddf2de..20e86a0 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -279,7 +279,7 @@
"liblog",
],
static_libs: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
@@ -287,6 +287,6 @@
"libui",
],
export_static_lib_headers: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
}
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index fecf5ae..d88d7c9 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -34,6 +34,8 @@
#include "FrameTracer/FrameTracer.h"
#include "TimeStats/TimeStats.h"
+#define EARLY_RELEASE_ENABLED false
+
namespace android {
using PresentState = frametimeline::SurfaceFrame::PresentState;
@@ -373,7 +375,7 @@
addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
mDrawingState.bufferSurfaceFrameTX.reset();
}
- } else if (mLastClientCompositionFence != nullptr) {
+ } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
mLastClientCompositionFence,
@@ -851,7 +853,15 @@
}
const Rect layerSize{getBounds()};
- return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
+ int32_t layerWidth = layerSize.getWidth();
+ int32_t layerHeight = layerSize.getHeight();
+
+ // Align the layer orientation with the buffer before comparism
+ if (mTransformHint & ui::Transform::ROT_90) {
+ std::swap(layerWidth, layerHeight);
+ }
+
+ return layerWidth != bufferWidth || layerHeight != bufferHeight;
}
void BufferStateLayer::decrementPendingBufferCount() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 579636f..8c164ed 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -55,6 +55,7 @@
MOCK_METHOD(void, setRequiresClientComposition,
(DisplayId displayId, bool requiresClientComposition), (override));
MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+ MOCK_METHOD(void, setPresentFenceTime, (nsecs_t presentFenceTime), (override));
MOCK_METHOD(void, setHwcPresentDelayedTime,
(DisplayId displayId,
std::chrono::steady_clock::time_point earliestFrameStartTime));
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index f844845..77dda6c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -238,7 +238,8 @@
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper != nullptr) {
- halWrapper->sendActualWorkDuration(*predictedDuration, systemTime());
+ halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(),
+ systemTime());
}
}
}
@@ -314,6 +315,10 @@
mExpectedPresentTimes.append(expectedPresentTime);
}
+void PowerAdvisor::setPresentFenceTime(nsecs_t presentFenceTime) {
+ mLastPresentFenceTime = presentFenceTime;
+}
+
void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) {
mFrameDelayDuration = frameDelayDuration;
}
@@ -383,11 +388,11 @@
// If we're predicting at the end of the frame, we use the current frame as a reference point
nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]);
- // We need an idea of when the last present fence fired and how long it made us wait
- // If we're predicting at the start of the frame, we want frame n-2's present fence time
- // If we're predicting at the end of the frame we want frame n-1's present time
- nsecs_t referenceFenceTime =
- (earlyHint ? mExpectedPresentTimes[-2] : mExpectedPresentTimes[-1]);
+ // When the prior frame should be presenting to the display
+ // If we're predicting at the start of the frame, we use last frame's expected present time
+ // If we're predicting at the end of the frame, the present fence time is already known
+ nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime);
+
// The timing info for the previously calculated display, if there was one
std::optional<DisplayTimeline> previousDisplayReferenceTiming;
std::vector<DisplayId>&& displayIds =
@@ -401,7 +406,11 @@
}
auto& displayData = mDisplayTimingData.at(displayId);
- referenceTiming = displayData.calculateDisplayTimeline(referenceFenceTime);
+
+ // mLastPresentFenceTime should always be the time of the reference frame, since it will be
+ // the previous frame's present fence if called at the start, and current frame's if called
+ // at the end
+ referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime);
// If this is the first display, include the duration before hwc present starts
if (!previousDisplayReferenceTiming.has_value()) {
@@ -411,14 +420,18 @@
previousDisplayReferenceTiming->hwcPresentEndTime;
}
- estimatedTiming = referenceTiming.estimateTimelineFromReference(mExpectedPresentTimes[-1],
- estimatedEndTime);
+ // Late hint can re-use reference timing here since it's estimating its own reference frame
+ estimatedTiming = earlyHint
+ ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime,
+ estimatedEndTime)
+ : referenceTiming;
+
// Update predicted present finish time with this display's present time
estimatedEndTime = estimatedTiming.hwcPresentEndTime;
// Track how long we spent waiting for the fence, can be excluded from the timing estimate
- idleDuration += estimatedTiming.probablyWaitsForReleaseFence
- ? mExpectedPresentTimes[-1] - estimatedTiming.releaseFenceWaitStartTime
+ idleDuration += estimatedTiming.probablyWaitsForPresentFence
+ ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime
: 0;
// Track how long we spent waiting to present, can be excluded from the timing estimate
@@ -475,15 +488,15 @@
// We don't predict waiting for vsync alignment yet
estimated.hwcPresentDelayDuration = 0;
- // For now just re-use last frame's post-present duration and assume it will not change much
// How long we expect to run before we start waiting for the fence
- // If it's the early hint we exclude time we spent waiting for a vsync, otherwise don't
- estimated.releaseFenceWaitStartTime = estimated.hwcPresentStartTime +
- (releaseFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration));
- estimated.probablyWaitsForReleaseFence = fenceTime > estimated.releaseFenceWaitStartTime;
- estimated.hwcPresentEndTime = postReleaseFenceHwcPresentDuration +
- (estimated.probablyWaitsForReleaseFence ? fenceTime
- : estimated.releaseFenceWaitStartTime);
+ // For now just re-use last frame's post-present duration and assume it will not change much
+ // Excludes time spent waiting for vsync since that's not going to be consistent
+ estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime +
+ (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration));
+ estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime;
+ estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration +
+ (estimated.probablyWaitsForPresentFence ? fenceTime
+ : estimated.presentFenceWaitStartTime);
return estimated;
}
@@ -509,16 +522,16 @@
// How long hwc present was delayed waiting for the next appropriate vsync
timeline.hwcPresentDelayDuration =
(waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0);
- // When we started waiting for the release fence after calling into hwc present
- timeline.releaseFenceWaitStartTime =
+ // When we started waiting for the present fence after calling into hwc present
+ timeline.presentFenceWaitStartTime =
timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay;
- timeline.probablyWaitsForReleaseFence = fenceTime > timeline.releaseFenceWaitStartTime &&
+ timeline.probablyWaitsForPresentFence = fenceTime > timeline.presentFenceWaitStartTime &&
fenceTime < timeline.hwcPresentEndTime;
// How long we ran after we finished waiting for the fence but before hwc present finished
- timeline.postReleaseFenceHwcPresentDuration = timeline.hwcPresentEndTime -
- (timeline.probablyWaitsForReleaseFence ? fenceTime
- : timeline.releaseFenceWaitStartTime);
+ timeline.postPresentFenceHwcPresentDuration = timeline.hwcPresentEndTime -
+ (timeline.probablyWaitsForPresentFence ? fenceTime
+ : timeline.presentFenceWaitStartTime);
return timeline;
}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index bdc7927..98921b0 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -70,7 +70,10 @@
// Reports the start and end times of a hwc present call this frame for a given display
virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
nsecs_t presentEndTime) = 0;
+ // Reports the expected time that the current frame will present to the display
virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0;
+ // Reports the most recent present fence time once it's known at the end of the frame
+ virtual void setPresentFenceTime(nsecs_t presentFenceTime) = 0;
// Reports whether a display used client composition this frame
virtual void setRequiresClientComposition(DisplayId displayId,
bool requiresClientComposition) = 0;
@@ -139,6 +142,7 @@
void setSkippedValidate(DisplayId displayId, bool skipped) override;
void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
void setExpectedPresentTime(nsecs_t expectedPresentTime) override;
+ void setPresentFenceTime(nsecs_t presentFenceTime) override;
void setHwcPresentDelayedTime(
DisplayId displayId,
std::chrono::steady_clock::time_point earliestFrameStartTime) override;
@@ -172,13 +176,13 @@
nsecs_t hwcPresentEndTime = -1;
// How long the actual hwc present was delayed after hwcPresentStartTime
nsecs_t hwcPresentDelayDuration = 0;
- // When we think we started waiting for the release fence after calling into hwc present and
+ // When we think we started waiting for the present fence after calling into hwc present and
// after potentially waiting for the earliest present time
- nsecs_t releaseFenceWaitStartTime = -1;
+ nsecs_t presentFenceWaitStartTime = -1;
// How long we ran after we finished waiting for the fence but before hwc present finished
- nsecs_t postReleaseFenceHwcPresentDuration = 0;
+ nsecs_t postPresentFenceHwcPresentDuration = 0;
// Are we likely to have waited for the present fence during composition
- bool probablyWaitsForReleaseFence = false;
+ bool probablyWaitsForPresentFence = false;
// Estimate one frame's timeline from that of a previous frame
DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime);
};
@@ -248,7 +252,9 @@
// Buffer of recent commit start times
RingBuffer<nsecs_t, 2> mCommitStartTimes;
// Buffer of recent expected present times
- RingBuffer<nsecs_t, 3> mExpectedPresentTimes;
+ RingBuffer<nsecs_t, 2> mExpectedPresentTimes;
+ // Most recent present fence time, set at the end of the frame once known
+ nsecs_t mLastPresentFenceTime = -1;
// Target for the entire pipeline including gpu
std::optional<nsecs_t> mTotalFrameTargetDuration;
// Updated list of display IDs
@@ -258,10 +264,8 @@
std::optional<bool> mSupportsPowerHint;
bool mPowerHintSessionRunning = false;
- // An adjustable safety margin which moves the "target" earlier to allow flinger to
- // go a bit over without dropping a frame, especially since we can't measure
- // the exact time hwc finishes composition so "actual" durations are measured
- // from the end of present() instead, which is a bit later.
+ // An adjustable safety margin which pads the "actual" value sent to PowerHAL,
+ // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error
static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms;
// How long we expect hwc to run after the present call until it waits for the fence
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 66beff2..3e69b5c 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -289,7 +289,7 @@
minTime = std::min(minTime, actuals.endTime);
}
if (actuals.presentTime != 0) {
- minTime = std::min(minTime, actuals.endTime);
+ minTime = std::min(minTime, actuals.presentTime);
}
return minTime;
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 68e0a96..6e9dad1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -148,6 +148,7 @@
mDrawingState.isTrustedOverlay = false;
mDrawingState.dropInputMode = gui::DropInputMode::NONE;
mDrawingState.dimmingEnabled = true;
+ mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -724,7 +725,7 @@
if (s.sequence != mLastCommittedTxSequence) {
// invalidate and recompute the visible regions if needed
- mLastCommittedTxSequence = s.sequence;
+ mLastCommittedTxSequence = s.sequence;
flags |= eVisibleRegion;
this->contentDirty = true;
@@ -732,6 +733,10 @@
mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering();
}
+ if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) {
+ mFlinger->mUpdateInputInfo = true;
+ }
+
commitTransaction(mDrawingState);
return flags;
@@ -882,7 +887,7 @@
if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false;
mDrawingState.isTrustedOverlay = isTrustedOverlay;
mDrawingState.modified = true;
- mFlinger->mInputInfoChanged = true;
+ mFlinger->mUpdateInputInfo = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -979,6 +984,13 @@
return true;
}
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
+ if (matrix.dsdx == mDrawingState.transform.dsdx() &&
+ matrix.dtdy == mDrawingState.transform.dtdy() &&
+ matrix.dtdx == mDrawingState.transform.dtdx() &&
+ matrix.dsdy == mDrawingState.transform.dsdy()) {
+ return false;
+ }
+
ui::Transform t;
t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
@@ -1090,6 +1102,19 @@
return Layer::PRIORITY_UNSET;
}
+bool Layer::setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility) {
+ if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
+ mDrawingState.defaultFrameRateCompatibility = compatibility;
+ mDrawingState.modified = true;
+ mFlinger->mScheduler->setDefaultFrameRateCompatibility(this);
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+scheduler::LayerInfo::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
+ return mDrawingState.defaultFrameRateCompatibility;
+}
+
bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
};
@@ -2053,7 +2078,7 @@
mDrawingState.inputInfo = info;
mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
mDrawingState.modified = true;
- mFlinger->mInputInfoChanged = true;
+ mFlinger->mUpdateInputInfo = true;
setTransactionFlags(eTransactionNeeded);
}
@@ -2632,6 +2657,8 @@
return FrameRateCompatibility::ExactOrMultiple;
case ANATIVEWINDOW_FRAME_RATE_EXACT:
return FrameRateCompatibility::Exact;
+ case ANATIVEWINDOW_FRAME_RATE_MIN:
+ return FrameRateCompatibility::Min;
case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
return FrameRateCompatibility::NoVote;
default:
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 911ab78..fb9b663 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -235,6 +235,9 @@
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
+ // Default frame rate compatibility used to set the layer refresh rate votetype.
+ FrameRateCompatibility defaultFrameRateCompatibility;
+
FrameRate frameRate;
// The combined frame rate of parents / children of this layer
@@ -438,6 +441,7 @@
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
virtual bool setDimmingEnabled(const bool dimmingEnabled);
+ virtual bool setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility);
virtual bool setFrameRateSelectionPriority(int32_t priority);
virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
virtual void setAutoRefresh(bool /* autoRefresh */) {}
@@ -446,6 +450,9 @@
// If the variable is not set on the layer, it traverses up the tree to inherit the frame
// rate priority from its parent.
virtual int32_t getFrameRateSelectionPriority() const;
+ //
+ virtual FrameRateCompatibility getDefaultFrameRateCompatibility() const;
+ //
virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 5f64efa..ae111c3 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -72,6 +72,20 @@
ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
}
+
+LayerHistory::LayerVoteType getVoteType(LayerInfo::FrameRateCompatibility compatibility,
+ bool contentDetectionEnabled) {
+ LayerHistory::LayerVoteType voteType;
+ if (!contentDetectionEnabled || compatibility == LayerInfo::FrameRateCompatibility::NoVote) {
+ voteType = LayerHistory::LayerVoteType::NoVote;
+ } else if (compatibility == LayerInfo::FrameRateCompatibility::Min) {
+ voteType = LayerHistory::LayerVoteType::Min;
+ } else {
+ voteType = LayerHistory::LayerVoteType::Heuristic;
+ }
+ return voteType;
+}
+
} // namespace
LayerHistory::LayerHistory()
@@ -81,10 +95,12 @@
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
+void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled) {
std::lock_guard lock(mLock);
LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != LayerStatus::NotFound,
"%s already registered", layer->getName().c_str());
+ LayerVoteType type =
+ getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled);
auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
// The layer can be placed on either map, it is assumed that partitionLayers() will be called
@@ -132,6 +148,22 @@
}
}
+void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDetectionEnabled) {
+ std::lock_guard lock(mLock);
+ auto id = layer->getSequence();
+
+ auto [found, layerPair] = findLayer(id);
+ if (found == LayerStatus::NotFound) {
+ // Offscreen layer
+ ALOGV("%s: %s not registered", __func__, layer->getName().c_str());
+ return;
+ }
+
+ const auto& info = layerPair->second;
+ info->setDefaultLayerVote(
+ getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled));
+}
+
auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> Summary {
Summary summary;
@@ -203,6 +235,8 @@
switch (frameRate.type) {
case Layer::FrameRateCompatibility::Default:
return LayerVoteType::ExplicitDefault;
+ case Layer::FrameRateCompatibility::Min:
+ return LayerVoteType::Min;
case Layer::FrameRateCompatibility::ExactOrMultiple:
return LayerVoteType::ExplicitExactOrMultiple;
case Layer::FrameRateCompatibility::NoVote:
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 7b6096f..12bec8d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -45,7 +45,7 @@
~LayerHistory();
// Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, LayerVoteType type);
+ void registerLayer(Layer*, bool contentDetectionEnabled);
// Sets the display size. Client is responsible for synchronization.
void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
@@ -63,6 +63,10 @@
// Marks the layer as active, and records the given state to its history.
void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
+ // Updates the default frame rate compatibility which takes effect when the app
+ // does not set a preference for refresh rate.
+ void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled);
+
using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 8a3b0b9..28cb24a 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -74,6 +74,8 @@
enum class FrameRateCompatibility {
Default, // Layer didn't specify any specific handling strategy
+ Min, // Layer needs the minimum frame rate.
+
Exact, // Layer needs the exact frame rate.
ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5dd9ad1..3181a7f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <ftl/fake_guard.h>
#include <gui/WindowInfo.h>
#include <system/window.h>
#include <ui/DisplayStatInfo.h>
@@ -94,9 +95,13 @@
}
void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
+ // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
{
- // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
- std::scoped_lock lock(mRefreshRateConfigsLock);
+ // mRefreshRateConfigsLock is not locked here to avoid the deadlock
+ // as the callback can attempt to acquire the lock before stopIdleTimer can finish
+ // the execution. It's safe to FakeGuard as main thread is the only thread that
+ // writes to the mRefreshRateConfigs.
+ ftl::FakeGuard guard(mRefreshRateConfigsLock);
if (mRefreshRateConfigs) {
mRefreshRateConfigs->stopIdleTimer();
mRefreshRateConfigs->clearIdleTimerCallbacks();
@@ -492,24 +497,10 @@
}
void Scheduler::registerLayer(Layer* layer) {
- using WindowType = gui::WindowInfo::Type;
-
- scheduler::LayerHistory::LayerVoteType voteType;
-
- if (!mFeatures.test(Feature::kContentDetection) ||
- layer->getWindowType() == WindowType::STATUS_BAR) {
- voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
- } else if (layer->getWindowType() == WindowType::WALLPAPER) {
- // Running Wallpaper at Min is considered as part of content detection.
- voteType = scheduler::LayerHistory::LayerVoteType::Min;
- } else {
- voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
- }
-
// If the content detection feature is off, we still keep the layer history,
// since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
- mLayerHistory.registerLayer(layer, voteType);
+ mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection));
}
void Scheduler::deregisterLayer(Layer* layer) {
@@ -530,6 +521,11 @@
mLayerHistory.setModeChangePending(pending);
}
+void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) {
+ mLayerHistory.setDefaultFrameRateCompatibility(layer,
+ mFeatures.test(Feature::kContentDetection));
+}
+
void Scheduler::chooseRefreshRateForContent() {
const auto configs = holdRefreshRateConfigs();
if (!configs->canSwitch()) return;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 587a773..7f76d1e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -178,6 +178,7 @@
void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
EXCLUDES(mRefreshRateConfigsLock);
void setModeChangePending(bool pending);
+ void setDefaultFrameRateCompatibility(Layer*);
void deregisterLayer(Layer*);
// Detects content using layer history, and selects a matching refresh rate.
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index e611658..3a918a1 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <scheduler/Fps.h>
#include <scheduler/Timer.h>
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9f4f0a2..ab5bb77 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -341,7 +341,7 @@
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
- mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make(*this)) {
+ mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()) {
ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
}
@@ -2256,6 +2256,7 @@
// Send a power hint hint after presentation is finished
if (mPowerHintSessionEnabled) {
+ mPowerAdvisor->setPresentFenceTime(mPreviousPresentFences[0].fenceTime->getSignalTime());
if (mPowerHintSessionMode.late) {
mPowerAdvisor->sendActualWorkDuration();
}
@@ -3114,6 +3115,7 @@
const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
if (!curr.isIdenticalTo(draw)) {
mVisibleRegionsDirty = true;
+ mUpdateInputInfo = true;
// find the displays that were removed
// (ie: in drawing state but not in current state)
@@ -3158,6 +3160,7 @@
if (mSomeChildrenChanged) {
mVisibleRegionsDirty = true;
mSomeChildrenChanged = false;
+ mUpdateInputInfo = true;
}
// Update transform hint.
@@ -3221,6 +3224,7 @@
mLayersAdded = false;
// Layers have been added.
mVisibleRegionsDirty = true;
+ mUpdateInputInfo = true;
}
// some layers might have been removed, so
@@ -3228,6 +3232,7 @@
if (mLayersRemoved) {
mLayersRemoved = false;
mVisibleRegionsDirty = true;
+ mUpdateInputInfo = true;
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (mLayersPendingRemoval.indexOf(layer) >= 0) {
// this layer is not visible anymore
@@ -3252,14 +3257,14 @@
std::vector<WindowInfo> windowInfos;
std::vector<DisplayInfo> displayInfos;
bool updateWindowInfo = false;
- if (mVisibleRegionsDirty || mInputInfoChanged) {
- mInputInfoChanged = false;
+ if (mUpdateInputInfo) {
+ mUpdateInputInfo = false;
updateWindowInfo = true;
buildWindowInfos(windowInfos, displayInfos);
- }
- if (!updateWindowInfo && mInputWindowCommands.empty()) {
+ } else if (mInputWindowCommands.empty()) {
return;
}
+
BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
windowInfos = std::move(windowInfos),
displayInfos = std::move(displayInfos),
@@ -3268,12 +3273,17 @@
inputFlinger = mInputFlinger, this]() {
ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
- mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,
- inputWindowCommands.syncInputWindows);
- } else if (inputWindowCommands.syncInputWindows) {
- // If the caller requested to sync input windows, but there are no
- // changes to input windows, notify immediately.
- windowInfosReported();
+ mWindowInfosListenerInvoker
+ ->windowInfosChanged(windowInfos, displayInfos,
+ inputWindowCommands.windowInfosReportedListeners);
+ } else {
+ // If there are listeners but no changes to input windows, call the listeners
+ // immediately.
+ for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) {
+ if (IInterface::asBinder(listener)->isBinderAlive()) {
+ listener->onWindowInfosReported();
+ }
+ }
}
for (const auto& focusRequest : inputWindowCommands.focusRequests) {
inputFlinger->setFocusedWindow(focusRequest);
@@ -4096,11 +4106,9 @@
Mutex::Autolock lock(mQueueLock);
// Generate a CountDownLatch pending state if this is a synchronous transaction.
- if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
- state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
- (state.inputWindowCommands.syncInputWindows
- ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction)
- : CountDownLatch::eSyncTransaction));
+ if (state.flags & eSynchronous) {
+ state.transactionCommittedSignal =
+ std::make_shared<CountDownLatch>(CountDownLatch::eSyncTransaction);
}
mTransactionQueue.emplace_back(state);
@@ -4558,6 +4566,14 @@
if (what & layer_state_t::eShadowRadiusChanged) {
if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(s.defaultFrameRateCompatibility);
+
+ if (layer->setDefaultFrameRateCompatibility(compatibility)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eFrameRateSelectionPriority) {
if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
flags |= eTraversalNeeded;
@@ -4608,7 +4624,7 @@
if (what & layer_state_t::eDropInputModeChanged) {
if (layer->setDropInputMode(s.dropInputMode)) {
flags |= eTraversalNeeded;
- mInputInfoChanged = true;
+ mUpdateInputInfo = true;
}
}
// This has to happen after we reparent children because when we reparent to null we remove
@@ -4915,6 +4931,9 @@
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) {
+ ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
+ mVisibleRegionsDirty = true;
+ scheduleRepaint();
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, refreshRate);
}
@@ -6798,11 +6817,6 @@
return future;
}
-void SurfaceFlinger::windowInfosReported() {
- Mutex::Autolock _l(mStateLock);
- signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
-}
-
// ---------------------------------------------------------------------------
void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5182ed8..ed6c8ce 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -333,7 +333,6 @@
// If set, disables reusing client composition buffers. This can be set by
// debug.sf.disable_client_composition_cache
bool mDisableClientCompositionCache = false;
- void windowInfosReported();
// Disables expensive rendering for all displays
// This is scheduled on the main thread
@@ -1198,7 +1197,7 @@
// Set during transaction application stage to track if the input info or children
// for a layer has changed.
// TODO: Also move visibleRegions over to a boolean system.
- bool mInputInfoChanged = false;
+ bool mUpdateInputInfo = false;
bool mSomeChildrenChanged;
bool mSomeDataspaceChanged = false;
bool mForceTransactionDisplayChange = false;
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 900d566..bbfeac1 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -106,7 +106,6 @@
public:
enum {
eSyncTransaction = 1 << 0,
- eSyncInputWindows = 1 << 1,
};
explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 30b9d8f..cc33001 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -26,21 +26,39 @@
using gui::IWindowInfosListener;
using gui::WindowInfo;
-struct WindowInfosListenerInvoker::WindowInfosReportedListener
- : gui::BnWindowInfosReportedListener {
- explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker) : mInvoker(invoker) {}
+struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowInfosReportedListener,
+ DeathRecipient {
+ explicit WindowInfosReportedListener(
+ size_t callbackCount,
+ const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+ SpHash<gui::IWindowInfosReportedListener>>&
+ windowInfosReportedListeners)
+ : mCallbacksPending(callbackCount),
+ mWindowInfosReportedListeners(windowInfosReportedListeners) {}
binder::Status onWindowInfosReported() override {
- mInvoker.windowInfosReported();
+ // TODO(b/222421815) There could potentially be callbacks that we don't need to wait for
+ // before calling the WindowInfosReportedListeners coming from InputWindowCommands. Filter
+ // the list of callbacks down to those from system server.
+ if (--mCallbacksPending == 0) {
+ for (const auto& listener : mWindowInfosReportedListeners) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ if (asBinder->isBinderAlive()) {
+ listener->onWindowInfosReported();
+ }
+ }
+ }
return binder::Status::ok();
}
- WindowInfosListenerInvoker& mInvoker;
-};
+ void binderDied(const wp<IBinder>&) { onWindowInfosReported(); }
-WindowInfosListenerInvoker::WindowInfosListenerInvoker(SurfaceFlinger& flinger)
- : mFlinger(flinger),
- mWindowInfosReportedListener(sp<WindowInfosReportedListener>::make(*this)) {}
+private:
+ std::atomic<size_t> mCallbacksPending;
+ std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+ SpHash<gui::IWindowInfosReportedListener>>
+ mWindowInfosReportedListeners;
+};
void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
sp<IBinder> asBinder = IInterface::asBinder(listener);
@@ -64,9 +82,11 @@
mWindowInfosListeners.erase(who);
}
-void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>& displayInfos,
- bool shouldSync) {
+void WindowInfosListenerInvoker::windowInfosChanged(
+ const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
+ const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+ SpHash<gui::IWindowInfosReportedListener>>&
+ windowInfosReportedListeners) {
ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
{
std::scoped_lock lock(mListenersMutex);
@@ -75,18 +95,25 @@
}
}
- mCallbacksPending = windowInfosListeners.size();
-
+ auto windowInfosReportedListener = windowInfosReportedListeners.empty()
+ ? nullptr
+ : sp<WindowInfosReportedListener>::make(windowInfosListeners.size(),
+ windowInfosReportedListeners);
for (const auto& listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos, displayInfos,
- shouldSync ? mWindowInfosReportedListener : nullptr);
- }
-}
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
-void WindowInfosListenerInvoker::windowInfosReported() {
- mCallbacksPending--;
- if (mCallbacksPending == 0) {
- mFlinger.windowInfosReported();
+ // linkToDeath is used here to ensure that the windowInfosReportedListeners
+ // are called even if one of the windowInfosListeners dies before
+ // calling onWindowInfosReported.
+ if (windowInfosReportedListener) {
+ asBinder->linkToDeath(windowInfosReportedListener);
+ }
+
+ auto status = listener->onWindowInfosChanged(windowInfos, displayInfos,
+ windowInfosReportedListener);
+ if (!status.isOk()) {
+ windowInfosReportedListener->onWindowInfosReported();
+ }
}
}
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index d8d8d0f..a1d66a1 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -29,22 +29,21 @@
class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
public:
- explicit WindowInfosListenerInvoker(SurfaceFlinger&);
-
void addWindowInfosListener(sp<gui::IWindowInfosListener>);
void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
void windowInfosChanged(const std::vector<gui::WindowInfo>&,
- const std::vector<gui::DisplayInfo>&, bool shouldSync);
+ const std::vector<gui::DisplayInfo>&,
+ const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+ SpHash<gui::IWindowInfosReportedListener>>&
+ windowInfosReportedListeners);
protected:
void binderDied(const wp<IBinder>& who) override;
private:
struct WindowInfosReportedListener;
- void windowInfosReported();
- SurfaceFlinger& mFlinger;
std::mutex mListenersMutex;
static constexpr size_t kStaticCapacity = 3;
@@ -52,7 +51,6 @@
mWindowInfosListeners GUARDED_BY(mListenersMutex);
sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
- std::atomic<size_t> mCallbacksPending{0};
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index f25043c..8e60247 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -151,7 +151,6 @@
sp<IBinder> handle = defaultServiceManager()->checkService(
String16(mFdp.ConsumeRandomLengthString().c_str()));
mFlinger->fromHandle(handle);
- mFlinger->windowInfosReported();
mFlinger->disableExpensiveRendering();
}
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index bc379f2..756db8a 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -2342,4 +2342,38 @@
EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f);
}
+TEST_F(FrameTimelineTest, getMinTime) {
+ // Use SurfaceFrame::getBaseTime to test the getMinTime.
+ FrameTimelineInfo ftInfo;
+
+ // Valid prediction state test.
+ ftInfo.vsyncId = 0L;
+ mTokenManager->generateTokenForPredictions({10});
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+ ASSERT_EQ(surfaceFrame->getBaseTime(), 10);
+
+ // Test prediction state which is not valid.
+ ftInfo.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
+ surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+ // Start time test.
+ surfaceFrame->setActualStartTime(200);
+ ASSERT_EQ(surfaceFrame->getBaseTime(), 200);
+
+ // End time test.
+ surfaceFrame->setAcquireFenceTime(100);
+ ASSERT_EQ(surfaceFrame->getBaseTime(), 100);
+
+ // Present time test.
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ presentFence->signalForTest(std::chrono::nanoseconds(50ns).count());
+ mFrameTimeline->setSfPresent(50, presentFence);
+ ASSERT_EQ(surfaceFrame->getBaseTime(), 50);
+}
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 17511cd..972198c 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -141,6 +141,54 @@
namespace {
+TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
+ .WillOnce(Return(LayerInfo::FrameRateCompatibility::NoVote));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(1, activeLayerCount());
+}
+
+TEST_F(LayerHistoryTest, singleLayerMinVoteDefaultCompatibility) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
+ .WillOnce(Return(LayerInfo::FrameRateCompatibility::Min));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+}
+
TEST_F(LayerHistoryTest, oneLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index ded7531..84f1170 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -107,22 +107,20 @@
EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
}
- void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- const FrameTimelineInfo& frameTimelineInfo) {
+ void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) {
mTransactionNumber++;
transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
- transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
transaction.desiredPresentTime = desiredPresentTime;
transaction.isAutoTimestamp = isAutoTimestamp;
transaction.frameTimelineInfo = frameTimelineInfo;
}
- void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
+ void NotPlacedOnTransactionQueue(uint32_t flags) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transaction;
- setupSingle(transaction, flags, syncInputWindows,
+ setupSingle(transaction, flags,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
FrameTimelineInfo{});
nsecs_t applicationTime = systemTime();
@@ -133,12 +131,10 @@
transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
transaction.id);
- // If transaction is synchronous or syncs input windows, SF
- // applyTransactionState should time out (5s) wating for SF to commit
- // the transaction or to receive a signal that syncInputWindows has
- // completed. If this is animation, it should not time out waiting.
+ // If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
+ // SF to commit the transaction. If this is animation, it should not time out waiting.
nsecs_t returnedTime = systemTime();
- if (flags & ISurfaceComposer::eSynchronous || syncInputWindows) {
+ if (flags & ISurfaceComposer::eSynchronous) {
EXPECT_GE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
} else {
EXPECT_LE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
@@ -148,7 +144,7 @@
EXPECT_EQ(1u, transactionQueue.size());
}
- void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
+ void PlaceOnTransactionQueue(uint32_t flags) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
@@ -156,8 +152,8 @@
// but afterwards it will look like the desired present time has passed
nsecs_t time = systemTime();
TransactionInfo transaction;
- setupSingle(transaction, flags, syncInputWindows,
- /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
+ setupSingle(transaction, flags, /*desiredPresentTime*/ time + s2ns(1), false,
+ FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
transaction.displays, transaction.flags,
@@ -167,7 +163,7 @@
transaction.id);
nsecs_t returnedTime = systemTime();
- if ((flags & ISurfaceComposer::eSynchronous) || syncInputWindows) {
+ if (flags & ISurfaceComposer::eSynchronous) {
EXPECT_GE(systemTime(),
applicationSentTime + mFlinger.getAnimationTransactionTimeout());
} else {
@@ -179,25 +175,21 @@
EXPECT_EQ(1u, transactionQueue.size());
}
- void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
+ void BlockedByPriorTransaction(uint32_t flags) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
nsecs_t time = systemTime();
- if (!syncInputWindows) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
- } else {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
- }
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
+
// transaction that should go on the pending thread
TransactionInfo transactionA;
- setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
+ setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ time + s2ns(1), false,
+ FrameTimelineInfo{});
// transaction that would not have gone on the pending thread if not
// blocked
TransactionInfo transactionB;
- setupSingle(transactionB, flags, syncInputWindows,
- /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
- FrameTimelineInfo{});
+ setupSingle(transactionB, flags, /*desiredPresentTime*/ systemTime(),
+ /*isAutoTimestamp*/ true, FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
@@ -226,8 +218,7 @@
// if this is an animation, this thread should be blocked for 5s
// in setTransactionState waiting for transactionA to flush. Otherwise,
// the transaction should be placed on the pending queue
- if (flags & (ISurfaceComposer::eSynchronous) ||
- syncInputWindows) {
+ if (flags & ISurfaceComposer::eSynchronous) {
EXPECT_GE(systemTime(),
applicationSentTime + mFlinger.getAnimationTransactionTimeout());
} else {
@@ -253,8 +244,8 @@
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
- setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ s2ns(1), false, FrameTimelineInfo{});
+ setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
+ FrameTimelineInfo{});
mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
@@ -285,31 +276,23 @@
}
TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
- NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+ NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous);
}
TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
- NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
+ NotPlacedOnTransactionQueue(/*flags*/ 0);
}
TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) {
- PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+ PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous);
}
TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
- PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
+ PlaceOnTransactionQueue(/*flags*/ 0);
}
TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) {
- BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
-}
-
-TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Animation) {
- BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
-}
-
-TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_SyncInputWindows) {
- BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true);
+ BlockedByPriorTransaction(ISurfaceComposer::eSynchronous);
}
TEST_F(TransactionApplicationTest, FromHandle) {
@@ -359,13 +342,11 @@
const std::vector<ComposerState>& states) {
TransactionInfo transaction;
const uint32_t kFlags = ISurfaceComposer::eSynchronous;
- const bool kSyncInputWindows = false;
const nsecs_t kDesiredPresentTime = systemTime();
const bool kIsAutoTimestamp = true;
const auto kFrameTimelineInfo = FrameTimelineInfo{};
- setupSingle(transaction, kFlags, kSyncInputWindows, kDesiredPresentTime, kIsAutoTimestamp,
- kFrameTimelineInfo);
+ setupSingle(transaction, kFlags, kDesiredPresentTime, kIsAutoTimestamp, kFrameTimelineInfo);
transaction.applyToken = applyToken;
for (const auto& state : states) {
transaction.states.push_back(state);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index e347883..d6dca45 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -53,6 +53,7 @@
MOCK_METHOD(void, setRequiresClientComposition,
(DisplayId displayId, bool requiresClientComposition), (override));
MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+ MOCK_METHOD(void, setPresentFenceTime, (nsecs_t presentFenceTime), (override));
MOCK_METHOD(void, setHwcPresentDelayedTime,
(DisplayId displayId,
std::chrono::steady_clock::time_point earliestFrameStartTime));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 0840a2f..d086d79 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -25,7 +25,10 @@
class MockLayer : public Layer {
public:
MockLayer(SurfaceFlinger* flinger, std::string name)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
+ EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
+ .WillOnce(testing::Return(scheduler::LayerInfo::FrameRateCompatibility::Default));
+ }
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
MOCK_CONST_METHOD0(getType, const char*());
@@ -33,6 +36,8 @@
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
+ MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility,
+ scheduler::LayerInfo::FrameRateCompatibility());
MOCK_CONST_METHOD0(getOwnerUid, uid_t());
MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
};
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index f866005..eb669c0 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -763,7 +763,11 @@
// We must support R8G8B8A8
std::vector<VkSurfaceFormatKHR> all_formats = {
{VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
+ {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
+ // Also allow to use PASS_THROUGH + HAL_DATASPACE_ARBITRARY
+ {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT},
+ {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT},
+ };
if (colorspace_ext) {
all_formats.emplace_back(VkSurfaceFormatKHR{