Merge "Guard against overflow errors for transparent regions"
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/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 3091f6b..6f7fea3 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -162,17 +162,16 @@
return 0;
}
bool newline = false;
+ int poll_timeout_ms = 30 * 1000;
while (true) {
- uint64_t start_time = Nanotime();
pollfd fds[] = { { .fd = fd, .events = POLLIN } };
- int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000));
+ int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), poll_timeout_ms));
if (ret == -1) {
dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno));
newline = true;
break;
- } else if (ret == 0) {
- uint64_t elapsed = Nanotime() - start_time;
- dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
+ } else if (ret == 0 && poll_timeout_ms != 0) {
+ dprintf(out_fd, "*** %s: Timed out after %ds\n", path, poll_timeout_ms / 1000 );
newline = true;
break;
} else {
@@ -189,6 +188,7 @@
break;
}
}
+ poll_timeout_ms = 0;
}
if (!newline) dprintf(out_fd, "\n");
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/include/input/PropertyMap.h b/include/input/PropertyMap.h
index 451918b..b1e3f85 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -18,10 +18,8 @@
#define _UTILS_PROPERTY_MAP_H
#include <android-base/result.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
#include <utils/Tokenizer.h>
+#include <unordered_map>
namespace android {
@@ -58,30 +56,27 @@
/* Adds a property.
* Replaces the property with the same key if it is already present.
*/
- void addProperty(const String8& key, const String8& value);
-
- /* Returns true if the property map contains the specified key. */
- bool hasProperty(const String8& key) const;
+ void addProperty(const std::string& key, const std::string& value);
/* Gets the value of a property and parses it.
* Returns true and sets outValue if the key was found and its value was parsed successfully.
* Otherwise returns false and does not modify outValue. (Also logs a warning.)
*/
- bool tryGetProperty(const String8& key, String8& outValue) const;
- bool tryGetProperty(const String8& key, bool& outValue) const;
- bool tryGetProperty(const String8& key, int32_t& outValue) const;
- bool tryGetProperty(const String8& key, float& outValue) const;
+ bool tryGetProperty(const std::string& key, std::string& outValue) const;
+ bool tryGetProperty(const std::string& key, bool& outValue) const;
+ bool tryGetProperty(const std::string& key, int32_t& outValue) const;
+ bool tryGetProperty(const std::string& key, float& outValue) const;
/* Adds all values from the specified property map. */
void addAll(const PropertyMap* map);
- /* Gets the underlying property map. */
- inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
-
/* Loads a property map from a file. */
static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename);
private:
+ /* Returns true if the property map contains the specified key. */
+ bool hasProperty(const std::string& key) const;
+
class Parser {
PropertyMap* mMap;
Tokenizer* mTokenizer;
@@ -95,11 +90,11 @@
status_t parseType();
status_t parseKey();
status_t parseKeyProperty();
- status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseModifier(const std::string& token, int32_t* outMetaState);
status_t parseCharacterLiteral(char16_t* outCharacter);
};
- KeyedVector<String8, String8> mProperties;
+ std::unordered_map<std::string, std::string> mProperties;
};
} // namespace android
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..82ebdd7 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;
@@ -336,6 +358,14 @@
LOG_ALWAYS_FATAL_IF(recipient == nullptr,
"linkToDeath(): recipient must be non-NULL");
+ if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0) {
+ ALOGW("Linking to death on %s but there are no threads (yet?) listening to incoming "
+ "transactions. See ProcessState::startThreadPool and "
+ "ProcessState::setThreadPoolMaxThreadCount. Generally you should setup the binder "
+ "threadpool before other initialization steps.",
+ String8(getInterfaceDescriptor()).c_str());
+ }
+
{
AutoMutex _l(mLock);
@@ -366,6 +396,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 +436,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 +509,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 +549,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 +606,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/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 7faff47..1f311ac 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -19,6 +19,7 @@
#include <binder/ProcessState.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
@@ -420,6 +421,9 @@
}
size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
+ pthread_mutex_lock(&mThreadCountLock);
+ base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); };
+
// may actually be one more than this, if join is called
if (mThreadPoolStarted) {
return mCurrentThreads < mKernelStartedThreads
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..9679a5f 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();
@@ -126,7 +130,7 @@
void* mVMStart;
// Protects thread count and wait variables below.
- pthread_mutex_t mThreadCountLock;
+ mutable pthread_mutex_t mThreadCountLock;
// Broadcast whenever mWaitingForThreads > 0
pthread_cond_t mThreadCountDecrement;
// Number of binder threads current executing a command.
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..fca3c29 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;
@@ -1196,12 +930,12 @@
size_t epochMsAfter = epochMillis();
- EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+ EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
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/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 2ca6ebd..0210237 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -7,6 +7,22 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aidl_interface {
+ name: "binderReadParcelIface",
+ host_supported: true,
+ unstable: true,
+ srcs: [
+ "EmptyParcelable.aidl",
+ "SingleDataParcelable.aidl",
+ "GenericDataParcelable.aidl",
+ ],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ },
+}
+
cc_fuzz {
name: "binder_parcel_fuzzer",
host_supported: true,
@@ -29,6 +45,8 @@
"libcutils",
"libhidlbase",
"liblog",
+ "binderReadParcelIface-cpp",
+ "binderReadParcelIface-ndk",
],
target: {
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
new file mode 100644
index 0000000..96d6223
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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 EmptyParcelable{
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
new file mode 100644
index 0000000..fc2542b
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
@@ -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.
+ */
+
+parcelable GenericDataParcelable {
+ int data;
+ float majorVersion;
+ float minorVersion;
+ IBinder binder;
+ ParcelFileDescriptor fileDescriptor;
+ int[] array;
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
new file mode 100644
index 0000000..d62891b
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 SingleDataParcelable{
+ int data;
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 7059d30..9dac2c9 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,6 +16,9 @@
#define FUZZ_LOG_TAG "binder"
#include "binder.h"
+#include "EmptyParcelable.h"
+#include "GenericDataParcelable.h"
+#include "SingleDataParcelable.h"
#include "util.h"
#include <android-base/hex.h>
@@ -354,6 +357,24 @@
status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result);
FUZZ_LOG() << " status: " << status << " result: " << result;
},
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
+ EmptyParcelable emptyParcelable{};
+ status_t status = emptyParcelable.readFromParcel(&p);
+ FUZZ_LOG() << " status: " << status;
+ },
+ [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
+ SingleDataParcelable singleDataParcelable;
+ status_t status = singleDataParcelable.readFromParcel(&p);
+ FUZZ_LOG() <<" status: " << status;
+ },
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
+ GenericDataParcelable genericDataParcelable;
+ status_t status = genericDataParcelable.readFromParcel(&p);
+ FUZZ_LOG() <<" status: " << status;
+ },
};
// clang-format on
#pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 26d6770..af773a0 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,6 +16,9 @@
#define FUZZ_LOG_TAG "binder_ndk"
#include "binder_ndk.h"
+#include "aidl/EmptyParcelable.h"
+#include "aidl/GenericDataParcelable.h"
+#include "aidl/SingleDataParcelable.h"
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
@@ -177,5 +180,24 @@
PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData),
PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData),
#undef COMMA
+
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
+ aidl::EmptyParcelable emptyParcelable;
+ binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
+ FUZZ_LOG() << "status: " << status;
+ },
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
+ aidl::SingleDataParcelable singleDataParcelable;
+ binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
+ FUZZ_LOG() << "status: " << status;
+ },
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
+ aidl::GenericDataParcelable genericDataParcelable;
+ binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
+ FUZZ_LOG() << "status: " << status;
+ },
};
// clang-format on
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/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
new file mode 100644
index 0000000..952b6a9
--- /dev/null
+++ b/libs/gui/fuzzer/Android.bp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+cc_defaults {
+ name: "libgui_fuzzer_defaults",
+ static_libs: [
+ "android.hidl.token@1.0-utils",
+ "libbinder_random_parcel",
+ "libgui_aidl_static",
+ "libgui_window_info_static",
+ "libpdx",
+ "libgmock",
+ "libgui_mocks",
+ "libgmock_ndk",
+ "libgmock_main",
+ "libgtest_ndk_c++",
+ "libgmock_main_ndk",
+ "librenderengine_mocks",
+ "perfetto_trace_protos",
+ "libcompositionengine_mocks",
+ "perfetto_trace_protos",
+ ],
+ shared_libs: [
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "android.hardware.power-V2-cpp",
+ "android.hidl.token@1.0",
+ "libSurfaceFlingerProp",
+ "libgui",
+ "libbase",
+ "liblog",
+ "libEGL",
+ "libGLESv2",
+ "libbinder",
+ "libcutils",
+ "libhidlbase",
+ "libinput",
+ "libui",
+ "libutils",
+ "libnativewindow",
+ "libvndksupport",
+ "libbufferhubqueue",
+ ],
+ header_libs: [
+ "libdvr_headers",
+ "libui_fuzzableDataspaces_headers",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
+
+cc_fuzz {
+ name: "libgui_surfaceComposer_fuzzer",
+ srcs: [
+ "libgui_surfaceComposer_fuzzer.cpp",
+ ],
+ defaults: [
+ "libgui_fuzzer_defaults",
+ ],
+}
diff --git a/libs/gui/fuzzer/README.md b/libs/gui/fuzzer/README.md
new file mode 100644
index 0000000..c9e8b65
--- /dev/null
+++ b/libs/gui/fuzzer/README.md
@@ -0,0 +1,42 @@
+# Fuzzers for Libgui
+
+## Table of contents
++ [libgui_surfaceComposer_fuzzer](#SurfaceComposer)
+
+# <a name="libgui_surfaceComposer_fuzzer"></a> Fuzzer for SurfaceComposer
+
+SurfaceComposer supports the following parameters:
+1. SurfaceWidth (parameter name:`width`)
+2. SurfaceHeight (parameter name:`height`)
+3. TransactionStateFlags (parameter name:`flags`)
+4. TransformHint (parameter name:`outTransformHint`)
+5. SurfacePixelFormat (parameter name:`format`)
+6. LayerId (parameter name:`outLayerId`)
+7. SurfaceComposerTags (parameter name:`surfaceTag`)
+8. PowerBoostID (parameter name:`boostId`)
+9. VsyncSource (parameter name:`vsyncSource`)
+10. EventRegistrationFlags (parameter name:`eventRegistration`)
+11. FrameRateCompatibility (parameter name:`frameRateCompatibility`)
+12. ChangeFrameRateStrategy (parameter name:`changeFrameRateStrategy`)
+13. HdrTypes (parameter name:`hdrTypes`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`surfaceTag` | 0.`BnSurfaceComposer::BOOT_FINISHED`, 1.`BnSurfaceComposer::CREATE_CONNECTION`, 2.`BnSurfaceComposer::GET_STATIC_DISPLAY_INFO`, 3.`BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION`, 4.`BnSurfaceComposer::CREATE_DISPLAY`, 5.`BnSurfaceComposer::DESTROY_DISPLAY`, 6.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN`, 7.`BnSurfaceComposer::SET_TRANSACTION_STATE`, 8.`BnSurfaceComposer::AUTHENTICATE_SURFACE`, 9.`BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS`, 10.`BnSurfaceComposer::GET_DISPLAY_STATE`, 11.`BnSurfaceComposer::CAPTURE_DISPLAY`, 12.`BnSurfaceComposer::CAPTURE_LAYERS`, 13.`BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS`, 14.`BnSurfaceComposer::GET_ANIMATION_FRAME_STATS`, 15.`BnSurfaceComposer::SET_POWER_MODE`, 16.`BnSurfaceComposer::GET_DISPLAY_STATS`, 17.`BnSurfaceComposer::SET_ACTIVE_COLOR_MODE`, 18.`BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS`, 19.`BnSurfaceComposer::INJECT_VSYNC`, 20.`BnSurfaceComposer::GET_LAYER_DEBUG_INFO`, 21.`BnSurfaceComposer::GET_COMPOSITION_PREFERENCE`, 22.`BnSurfaceComposer::GET_COLOR_MANAGEMENT`, 23.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES`, 24.`BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED`, 25.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE`, 26.`BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT`, 27.`BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY`, 28.`BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES`, 29.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS`, 30.`BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER`, 31.`BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER`, 32.`BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS`, 33.`BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS`, 34.`BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT`, 35.`BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS`, 36.`BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID`, 37.`BnSurfaceComposer::NOTIFY_POWER_BOOST`, 38.`BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS`, 39.`BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE`, 40.`BnSurfaceComposer::SET_GAME_CONTENT_TYPE`, 41.`BnSurfaceComposer::SET_FRAME_RATE`, 42.`BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN`, 43.`BnSurfaceComposer::SET_FRAME_TIMELINE_INFO`, 44.`BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER`, 45.`BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY`, 46.`BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT`, 47.`BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO`, 48.`BnSurfaceComposer::ADD_FPS_LISTENER`, 49.`BnSurfaceComposer::REMOVE_FPS_LISTENER`, 50.`BnSurfaceComposer::OVERRIDE_HDR_TYPES`, 51.`BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER`, 52.`BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER`, 53.`BnSurfaceComposer::ON_PULL_ATOM`, 54.`BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER`, 55.`BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER` | Value obtained from FuzzedDataProvider|
+|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider|
+|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider|
+|`eventRegistration`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride` |Value obtained from FuzzedDataProvider|
+|`frameRateCompatibility`| 0.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT`, 1.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE` |Value obtained from FuzzedDataProvider|
+|`changeFrameRateStrategy`| 0.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS`, 1.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS` |Value obtained from FuzzedDataProvider|
+|`hdrTypes`| 0.`ui::Hdr::DOLBY_VISION`, 1.`ui::Hdr::HDR10`, 2.`ui::Hdr::HLG`, 3.`ui::Hdr::HDR10_PLUS` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) libgui_surfaceComposer_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/libgui_surfaceComposer_fuzzer/libgui_surfaceComposer_fuzzer
+```
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
new file mode 100644
index 0000000..98e2ea3
--- /dev/null
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/gui/BnRegionSamplingListener.h>
+#include <android/gui/BnSurfaceComposer.h>
+#include <android/gui/BnSurfaceComposerClient.h>
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/ISurfaceComposerClient.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gmock/gmock.h>
+#include <gui/BLASTBufferQueue.h>
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/LayerState.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
+#include <ui/fuzzer/FuzzableDataspaces.h>
+
+namespace android {
+
+constexpr uint32_t kOrientation[] = {
+ ui::Transform::ROT_0, ui::Transform::FLIP_H, ui::Transform::FLIP_V,
+ ui::Transform::ROT_90, ui::Transform::ROT_180, ui::Transform::ROT_270,
+};
+
+Rect getRect(FuzzedDataProvider* fdp) {
+ const int32_t left = fdp->ConsumeIntegral<int32_t>();
+ const int32_t top = fdp->ConsumeIntegral<int32_t>();
+ const int32_t right = fdp->ConsumeIntegral<int32_t>();
+ const int32_t bottom = fdp->ConsumeIntegral<int32_t>();
+ return Rect(left, top, right, bottom);
+}
+
+gui::DisplayBrightness getBrightness(FuzzedDataProvider* fdp) {
+ static constexpr float kMinBrightness = 0;
+ static constexpr float kMaxBrightness = 1;
+ gui::DisplayBrightness brightness;
+ brightness.sdrWhitePoint =
+ fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
+ brightness.sdrWhitePointNits =
+ fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
+ brightness.displayBrightness =
+ fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
+ brightness.displayBrightnessNits =
+ fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
+ return brightness;
+}
+
+class FakeBnSurfaceComposer : public gui::BnSurfaceComposer {
+public:
+ MOCK_METHOD(binder::Status, bootFinished, (), (override));
+ MOCK_METHOD(binder::Status, createDisplayEventConnection,
+ (gui::ISurfaceComposer::VsyncSource, gui::ISurfaceComposer::EventRegistration,
+ sp<gui::IDisplayEventConnection>*),
+ (override));
+ MOCK_METHOD(binder::Status, createConnection, (sp<gui::ISurfaceComposerClient>*), (override));
+ MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, sp<IBinder>*),
+ (override));
+ MOCK_METHOD(binder::Status, destroyDisplay, (const sp<IBinder>&), (override));
+ MOCK_METHOD(binder::Status, getPhysicalDisplayIds, (std::vector<int64_t>*), (override));
+ MOCK_METHOD(binder::Status, getPrimaryPhysicalDisplayId, (int64_t*), (override));
+ MOCK_METHOD(binder::Status, getPhysicalDisplayToken, (int64_t, sp<IBinder>*), (override));
+ MOCK_METHOD(binder::Status, setPowerMode, (const sp<IBinder>&, int), (override));
+ MOCK_METHOD(binder::Status, getSupportedFrameTimestamps, (std::vector<FrameEvent>*),
+ (override));
+ MOCK_METHOD(binder::Status, getDisplayStats, (const sp<IBinder>&, gui::DisplayStatInfo*),
+ (override));
+ MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*),
+ (override));
+ MOCK_METHOD(binder::Status, getStaticDisplayInfo, (const sp<IBinder>&, gui::StaticDisplayInfo*),
+ (override));
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfo,
+ (const sp<IBinder>&, gui::DynamicDisplayInfo*), (override));
+ MOCK_METHOD(binder::Status, getDisplayNativePrimaries,
+ (const sp<IBinder>&, gui::DisplayPrimaries*), (override));
+ MOCK_METHOD(binder::Status, setActiveColorMode, (const sp<IBinder>&, int), (override));
+ MOCK_METHOD(binder::Status, setBootDisplayMode, (const sp<IBinder>&, int), (override));
+ MOCK_METHOD(binder::Status, clearBootDisplayMode, (const sp<IBinder>&), (override));
+ MOCK_METHOD(binder::Status, getBootDisplayModeSupport, (bool*), (override));
+ MOCK_METHOD(binder::Status, setAutoLowLatencyMode, (const sp<IBinder>&, bool), (override));
+ MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override));
+ MOCK_METHOD(binder::Status, captureDisplay,
+ (const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
+ MOCK_METHOD(binder::Status, captureDisplayById, (int64_t, const sp<IScreenCaptureListener>&),
+ (override));
+ MOCK_METHOD(binder::Status, captureLayers,
+ (const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
+ MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override));
+ MOCK_METHOD(binder::Status, getAnimationFrameStats, (gui::FrameStats*), (override));
+ MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&),
+ (override));
+ MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override));
+ MOCK_METHOD(binder::Status, enableVSyncInjections, (bool), (override));
+ MOCK_METHOD(binder::Status, injectVSync, (int64_t), (override));
+ MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override));
+ MOCK_METHOD(binder::Status, getColorManagement, (bool*), (override));
+ MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*),
+ (override));
+ MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes,
+ (const sp<IBinder>&, gui::ContentSamplingAttributes*), (override));
+ MOCK_METHOD(binder::Status, setDisplayContentSamplingEnabled,
+ (const sp<IBinder>&, bool, int8_t, int64_t), (override));
+ MOCK_METHOD(binder::Status, getDisplayedContentSample,
+ (const sp<IBinder>&, int64_t, int64_t, gui::DisplayedFrameStats*), (override));
+ MOCK_METHOD(binder::Status, getProtectedContentSupport, (bool*), (override));
+ MOCK_METHOD(binder::Status, isWideColorDisplay, (const sp<IBinder>&, bool*), (override));
+ MOCK_METHOD(binder::Status, addRegionSamplingListener,
+ (const gui::ARect&, const sp<IBinder>&, const sp<gui::IRegionSamplingListener>&),
+ (override));
+ MOCK_METHOD(binder::Status, removeRegionSamplingListener,
+ (const sp<gui::IRegionSamplingListener>&), (override));
+ MOCK_METHOD(binder::Status, addFpsListener, (int32_t, const sp<gui::IFpsListener>&),
+ (override));
+ MOCK_METHOD(binder::Status, removeFpsListener, (const sp<gui::IFpsListener>&), (override));
+ MOCK_METHOD(binder::Status, addTunnelModeEnabledListener,
+ (const sp<gui::ITunnelModeEnabledListener>&), (override));
+ MOCK_METHOD(binder::Status, removeTunnelModeEnabledListener,
+ (const sp<gui::ITunnelModeEnabledListener>&), (override));
+ MOCK_METHOD(binder::Status, setDesiredDisplayModeSpecs,
+ (const sp<IBinder>&, int32_t, bool, float, float, float,
+ float appRequestRefreshRateMax),
+ (override));
+ MOCK_METHOD(binder::Status, getDesiredDisplayModeSpecs,
+ (const sp<IBinder>&, gui::DisplayModeSpecs*), (override));
+ MOCK_METHOD(binder::Status, getDisplayBrightnessSupport, (const sp<IBinder>&, bool*),
+ (override));
+ MOCK_METHOD(binder::Status, setDisplayBrightness,
+ (const sp<IBinder>&, const gui::DisplayBrightness&), (override));
+ MOCK_METHOD(binder::Status, addHdrLayerInfoListener,
+ (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override));
+ MOCK_METHOD(binder::Status, removeHdrLayerInfoListener,
+ (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override));
+ MOCK_METHOD(binder::Status, notifyPowerBoost, (int), (override));
+ MOCK_METHOD(binder::Status, setGlobalShadowSettings,
+ (const gui::Color&, const gui::Color&, float, float, float), (override));
+ MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
+ (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
+ MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
+ MOCK_METHOD(binder::Status, addTransactionTraceListener,
+ (const sp<gui::ITransactionTraceListener>&), (override));
+ MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
+ MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
+ MOCK_METHOD(binder::Status, addWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
+ (override));
+ MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
+ (override));
+};
+
+class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
+public:
+ MOCK_METHOD(binder::Status, createSurface,
+ (const std::string& name, int32_t flags, const sp<IBinder>& parent,
+ const gui::LayerMetadata& metadata, gui::CreateSurfaceResult* outResult),
+ (override));
+
+ MOCK_METHOD(binder::Status, clearLayerFrameStats, (const sp<IBinder>& handle), (override));
+
+ MOCK_METHOD(binder::Status, getLayerFrameStats,
+ (const sp<IBinder>& handle, gui::FrameStats* outStats), (override));
+
+ MOCK_METHOD(binder::Status, mirrorSurface,
+ (const sp<IBinder>& mirrorFromHandle, gui::MirrorSurfaceResult* outResult),
+ (override));
+};
+
+class FakeDisplayEventDispatcher : public DisplayEventDispatcher {
+public:
+ FakeDisplayEventDispatcher(const sp<Looper>& looper,
+ gui::ISurfaceComposer::VsyncSource vsyncSource,
+ gui::ISurfaceComposer::EventRegistration eventRegistration)
+ : DisplayEventDispatcher(looper, vsyncSource, eventRegistration){};
+
+ MOCK_METHOD4(dispatchVsync, void(nsecs_t, PhysicalDisplayId, uint32_t, VsyncEventData));
+ MOCK_METHOD3(dispatchHotplug, void(nsecs_t, PhysicalDisplayId, bool));
+ MOCK_METHOD4(dispatchModeChanged, void(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t));
+ MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId));
+ MOCK_METHOD3(dispatchFrameRateOverrides,
+ void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>));
+};
+
+} // namespace android
+
+namespace android::hardware {
+
+namespace graphics::bufferqueue::V1_0::utils {
+
+class FakeGraphicBufferProducerV1 : public HGraphicBufferProducer {
+public:
+ FakeGraphicBufferProducerV1() {
+ ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return 0; });
+ ON_CALL(*this, setAsyncMode).WillByDefault([]() { return 0; });
+ ON_CALL(*this, detachBuffer).WillByDefault([]() { return 0; });
+ ON_CALL(*this, cancelBuffer).WillByDefault([]() { return 0; });
+ ON_CALL(*this, disconnect).WillByDefault([]() { return 0; });
+ ON_CALL(*this, setSidebandStream).WillByDefault([]() { return 0; });
+ ON_CALL(*this, allowAllocation).WillByDefault([]() { return 0; });
+ ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return 0; });
+ ON_CALL(*this, setSharedBufferMode).WillByDefault([]() { return 0; });
+ ON_CALL(*this, setAutoRefresh).WillByDefault([]() { return 0; });
+ ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return 0; });
+ ON_CALL(*this, setLegacyBufferDrop).WillByDefault([]() { return 0; });
+ };
+ MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb));
+ MOCK_METHOD1(setMaxDequeuedBufferCount, Return<int32_t>(int32_t));
+ MOCK_METHOD1(setAsyncMode, Return<int32_t>(bool));
+ MOCK_METHOD6(dequeueBuffer,
+ Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t,
+ bool, dequeueBuffer_cb));
+ MOCK_METHOD1(detachBuffer, Return<int32_t>(int));
+ MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb));
+ MOCK_METHOD2(attachBuffer, Return<void>(const media::V1_0::AnwBuffer&, attachBuffer_cb));
+ MOCK_METHOD3(
+ queueBuffer,
+ Return<void>(
+ int,
+ const graphics::bufferqueue::V1_0::IGraphicBufferProducer::QueueBufferInput&,
+ queueBuffer_cb));
+ MOCK_METHOD2(cancelBuffer, Return<int32_t>(int, const hidl_handle&));
+ MOCK_METHOD2(query, Return<void>(int32_t, query_cb));
+ MOCK_METHOD4(connect,
+ Return<void>(const sp<graphics::bufferqueue::V1_0::IProducerListener>&, int32_t,
+ bool, connect_cb));
+ MOCK_METHOD2(disconnect,
+ Return<int32_t>(
+ int, graphics::bufferqueue::V1_0::IGraphicBufferProducer::DisconnectMode));
+ MOCK_METHOD1(setSidebandStream, Return<int32_t>(const hidl_handle&));
+ MOCK_METHOD4(allocateBuffers,
+ Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t));
+ MOCK_METHOD1(allowAllocation, Return<int32_t>(bool));
+ MOCK_METHOD1(setGenerationNumber, Return<int32_t>(uint32_t));
+ MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb));
+ MOCK_METHOD1(setSharedBufferMode, Return<int32_t>(bool));
+ MOCK_METHOD1(setAutoRefresh, Return<int32_t>(bool));
+ MOCK_METHOD1(setDequeueTimeout, Return<int32_t>(nsecs_t));
+ MOCK_METHOD1(setLegacyBufferDrop, Return<int32_t>(bool));
+ MOCK_METHOD1(getLastQueuedBuffer, Return<void>(getLastQueuedBuffer_cb));
+ MOCK_METHOD1(getFrameTimestamps, Return<void>(getFrameTimestamps_cb));
+ MOCK_METHOD1(getUniqueId, Return<void>(getUniqueId_cb));
+};
+
+}; // namespace graphics::bufferqueue::V1_0::utils
+
+namespace graphics::bufferqueue::V2_0::utils {
+
+class FakeGraphicBufferProducerV2 : public HGraphicBufferProducer {
+public:
+ FakeGraphicBufferProducerV2() {
+ ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, setAsyncMode).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, detachBuffer).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, cancelBuffer).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, disconnect).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, allocateBuffers).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, allowAllocation).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return Status::OK; });
+ ON_CALL(*this, getUniqueId).WillByDefault([]() { return 0; });
+ };
+ MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb));
+ MOCK_METHOD1(setMaxDequeuedBufferCount, Return<graphics::bufferqueue::V2_0::Status>(int));
+ MOCK_METHOD1(setAsyncMode, Return<graphics::bufferqueue::V2_0::Status>(bool));
+ MOCK_METHOD2(
+ dequeueBuffer,
+ Return<void>(
+ const graphics::bufferqueue::V2_0::IGraphicBufferProducer::DequeueBufferInput&,
+ dequeueBuffer_cb));
+ MOCK_METHOD1(detachBuffer, Return<graphics::bufferqueue::V2_0::Status>(int));
+ MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb));
+ MOCK_METHOD3(attachBuffer,
+ Return<void>(const graphics::common::V1_2::HardwareBuffer&, uint32_t,
+ attachBuffer_cb));
+ MOCK_METHOD3(
+ queueBuffer,
+ Return<void>(
+ int,
+ const graphics::bufferqueue::V2_0::IGraphicBufferProducer::QueueBufferInput&,
+ queueBuffer_cb));
+ MOCK_METHOD2(cancelBuffer,
+ Return<graphics::bufferqueue::V2_0::Status>(int, const hidl_handle&));
+ MOCK_METHOD2(query, Return<void>(int32_t, query_cb));
+ MOCK_METHOD4(connect,
+ Return<void>(const sp<graphics::bufferqueue::V2_0::IProducerListener>&,
+ graphics::bufferqueue::V2_0::ConnectionType, bool, connect_cb));
+ MOCK_METHOD1(disconnect,
+ Return<graphics::bufferqueue::V2_0::Status>(
+ graphics::bufferqueue::V2_0::ConnectionType));
+ MOCK_METHOD4(allocateBuffers,
+ Return<graphics::bufferqueue::V2_0::Status>(uint32_t, uint32_t, uint32_t,
+ uint64_t));
+ MOCK_METHOD1(allowAllocation, Return<graphics::bufferqueue::V2_0::Status>(bool));
+ MOCK_METHOD1(setGenerationNumber, Return<graphics::bufferqueue::V2_0::Status>(uint32_t));
+ MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb));
+ MOCK_METHOD1(setDequeueTimeout, Return<graphics::bufferqueue::V2_0::Status>(int64_t));
+ MOCK_METHOD0(getUniqueId, Return<uint64_t>());
+};
+
+}; // namespace graphics::bufferqueue::V2_0::utils
+}; // namespace android::hardware
diff --git a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp
new file mode 100644
index 0000000..6d5427b
--- /dev/null
+++ b/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <libgui_fuzzer_utils.h>
+
+using namespace android;
+
+class SurfaceComposerFuzzer {
+public:
+ SurfaceComposerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+private:
+ FuzzedDataProvider mFdp;
+};
+
+void SurfaceComposerFuzzer::process() {
+ sp<FakeBnSurfaceComposer> composer(new FakeBnSurfaceComposer());
+ fuzzService(composer.get(), std::move(mFdp));
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ SurfaceComposerFuzzer surfaceComposerFuzzer(data, size);
+ surfaceComposerFuzzer.process();
+ return 0;
+}
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/Keyboard.cpp b/libs/input/Keyboard.cpp
index c3f5151..3f8467d 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -49,25 +49,23 @@
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
- String8 keyLayoutName;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
- keyLayoutName)) {
+ std::string keyLayoutName;
+ if (deviceConfiguration->tryGetProperty("keyboard.layout", keyLayoutName)) {
status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
- "it was not found.",
- deviceIdentifier.name.c_str(), keyLayoutName.string());
+ "it was not found.",
+ deviceIdentifier.name.c_str(), keyLayoutName.c_str());
}
}
- String8 keyCharacterMapName;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
- keyCharacterMapName)) {
+ std::string keyCharacterMapName;
+ if (deviceConfiguration->tryGetProperty("keyboard.characterMap", keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
- "map '%s' but it was not found.",
- deviceIdentifier.name.c_str(), keyCharacterMapName.string());
+ "map '%s' but it was not found.",
+ deviceIdentifier.name.c_str(), keyCharacterMapName.c_str());
}
}
@@ -165,7 +163,7 @@
return false;
}
bool isSpecialFunction = false;
- config->tryGetProperty(String8("keyboard.specialFunction"), isSpecialFunction);
+ config->tryGetProperty("keyboard.specialFunction", isSpecialFunction);
return isSpecialFunction;
}
@@ -180,8 +178,7 @@
if (deviceConfiguration) {
bool builtIn = false;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
- && builtIn) {
+ if (deviceConfiguration->tryGetProperty("keyboard.builtIn", builtIn) && builtIn) {
return true;
}
}
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index a842166..662e568 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "PropertyMap"
#include <input/PropertyMap.h>
+#include <log/log.h>
// Enables debug output for the parser.
#define DEBUG_PARSER 0
@@ -39,25 +40,25 @@
mProperties.clear();
}
-void PropertyMap::addProperty(const String8& key, const String8& value) {
- mProperties.add(key, value);
+void PropertyMap::addProperty(const std::string& key, const std::string& value) {
+ mProperties.emplace(key, value);
}
-bool PropertyMap::hasProperty(const String8& key) const {
- return mProperties.indexOfKey(key) >= 0;
+bool PropertyMap::hasProperty(const std::string& key) const {
+ return mProperties.find(key) != mProperties.end();
}
-bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
- ssize_t index = mProperties.indexOfKey(key);
- if (index < 0) {
+bool PropertyMap::tryGetProperty(const std::string& key, std::string& outValue) const {
+ auto it = mProperties.find(key);
+ if (it == mProperties.end()) {
return false;
}
- outValue = mProperties.valueAt(index);
+ outValue = it->second;
return true;
}
-bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+bool PropertyMap::tryGetProperty(const std::string& key, bool& outValue) const {
int32_t intValue;
if (!tryGetProperty(key, intValue)) {
return false;
@@ -67,34 +68,34 @@
return true;
}
-bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
- String8 stringValue;
+bool PropertyMap::tryGetProperty(const std::string& key, int32_t& outValue) const {
+ std::string stringValue;
if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
return false;
}
char* end;
- int value = strtol(stringValue.string(), &end, 10);
+ int value = strtol(stringValue.c_str(), &end, 10);
if (*end != '\0') {
- ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(),
- stringValue.string());
+ ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.c_str(),
+ stringValue.c_str());
return false;
}
outValue = value;
return true;
}
-bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
- String8 stringValue;
+bool PropertyMap::tryGetProperty(const std::string& key, float& outValue) const {
+ std::string stringValue;
if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
return false;
}
char* end;
- float value = strtof(stringValue.string(), &end);
+ float value = strtof(stringValue.c_str(), &end);
if (*end != '\0') {
- ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(),
- stringValue.string());
+ ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.c_str(),
+ stringValue.c_str());
return false;
}
outValue = value;
@@ -102,8 +103,8 @@
}
void PropertyMap::addAll(const PropertyMap* map) {
- for (size_t i = 0; i < map->mProperties.size(); i++) {
- mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+ for (const auto& [key, value] : map->mProperties) {
+ mProperties.emplace(key, value);
}
}
@@ -184,13 +185,13 @@
return BAD_VALUE;
}
- if (mMap->hasProperty(keyToken)) {
+ if (mMap->hasProperty(keyToken.string())) {
ALOGE("%s: Duplicate property value for key '%s'.",
mTokenizer->getLocation().string(), keyToken.string());
return BAD_VALUE;
}
- mMap->addProperty(keyToken, valueToken);
+ mMap->addProperty(keyToken.string(), valueToken.string());
}
mTokenizer->nextLine();
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
index afb97a1..d985dc1 100755
--- a/libs/input/PropertyMap_fuzz.cpp
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -17,32 +17,22 @@
#include "android-base/file.h"
#include "fuzzer/FuzzedDataProvider.h"
#include "input/PropertyMap.h"
-#include "utils/String8.h"
static constexpr int MAX_FILE_SIZE = 256;
static constexpr int MAX_STR_LEN = 2048;
static constexpr int MAX_OPERATIONS = 1000;
-static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap&)>>
operations = {
- [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
- propertyMap.getProperties();
- },
- [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ [](FuzzedDataProvider*, android::PropertyMap& propertyMap) -> void {
propertyMap.clear();
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
- std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- android::String8 key = android::String8(keyStr.c_str());
- propertyMap.hasProperty(key);
- },
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
- std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- android::String8 key = android::String8(keyStr.c_str());
- android::String8 out;
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
+ std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ std::string out;
propertyMap.tryGetProperty(key, out);
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void {
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap& /*unused*/) -> void {
TemporaryFile tf;
// Generate file contents
std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
@@ -54,17 +44,15 @@
}
android::PropertyMap::load(tf.path);
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
- std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- android::String8 key = android::String8(keyStr.c_str());
- android::String8 val = android::String8(valStr.c_str());
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
+ std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ std::string val = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
propertyMap.addProperty(key, val);
},
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider dataProvider(data, size);
- android::PropertyMap propertyMap = android::PropertyMap();
+ android::PropertyMap propertyMap;
int opsRun = 0;
while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
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/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 22dd866..6dc01b9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -921,7 +921,8 @@
// Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
// and the middle part without rounded corners.
- const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+ const int32_t radius = ceil(
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
setScissor(topRect);
drawMesh(mesh);
@@ -1266,23 +1267,24 @@
const half3 solidColor = layer.source.solidColor;
const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ const float radius =
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
+ 2.0f;
// Buffer sources will have a black solid color ignored in the shader,
// so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
if (layer.disableBlending) {
glDisable(GL_BLEND);
}
setSourceDataSpace(layer.sourceDataspace);
if (layer.shadow.length > 0.0f) {
- handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
- layer.shadow);
+ handleShadow(layer.geometry.boundaries, radius, layer.shadow);
}
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
handleRoundedCorners(display, layer, mesh);
} else {
drawMesh(mesh);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 154e526..b3a617c 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -87,7 +87,7 @@
// rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
// in local layer coordinate space, so we have to take the layer transform into account when
// walking up the tree.
- float roundedCornersRadius = 0.0;
+ vec2 roundedCornersRadius = vec2(0.0f, 0.0f);
// Rectangle within which corners will be rounded.
FloatRect roundedCornersCrop = FloatRect();
@@ -258,7 +258,8 @@
PrintTo(settings.boundaries, os);
*os << "\n .positionTransform = ";
PrintMatrix(settings.positionTransform, os);
- *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersRadiusX = " << settings.roundedCornersRadius.x;
+ *os << "\n .roundedCornersRadiusY = " << settings.roundedCornersRadius.y;
*os << "\n .roundedCornersCrop = ";
PrintTo(settings.roundedCornersCrop, os);
*os << "\n}";
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f3064f3..c39f0a9 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -66,7 +66,7 @@
Geometry{
.boundaries = rect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
// drawShadow ignores alpha
.shadow =
@@ -87,7 +87,7 @@
Geometry{
.boundaries = smallerRect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
.source =
PixelSource{
@@ -148,7 +148,7 @@
// In reduced shader mode, all non-zero round rect radii get the same code path.
for (float roundedCornersRadius : {0.0f, 50.0f}) {
// roundedCornersCrop is always set, but the radius triggers the behavior
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
for (bool isOpaque : {true, false}) {
layer.source.buffer.isOpaque = isOpaque;
for (auto alpha : {half(.2f), half(1.0f)}) {
@@ -181,7 +181,7 @@
for (auto transform : {mat4(), kScaleAndTranslate}) {
layer.geometry.positionTransform = transform;
for (float roundedCornersRadius : {0.0f, 50.f}) {
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
auto layers = std::vector<LayerSettings>{layer};
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
base::unique_fd());
@@ -238,7 +238,7 @@
.geometry =
Geometry{
.boundaries = rect,
- .roundedCornersRadius = 27, // larger than the 20 above.
+ .roundedCornersRadius = {27.f, 27.f},
.roundedCornersCrop =
FloatRect(0, 0, displayRect.width(), displayRect.height()),
},
@@ -275,9 +275,9 @@
// larger than the layer bounds.
.positionTransform = kFlip,
.boundaries = rect,
- .roundedCornersRadius = 94.2551,
- .roundedCornersCrop = FloatRect(
- -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
+ .roundedCornersRadius = {94.2551f, 94.2551f},
+ .roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75,
+ displayRect.height()),
},
.source = PixelSource{.buffer =
Buffer{
@@ -307,10 +307,11 @@
// the boundaries have to be smaller than the rounded crop so that
// clipRRect is used instead of drawRRect
.boundaries = small,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
.roundedCornersCrop = rect,
},
- .source = PixelSource{
+ .source =
+ PixelSource{
.solidColor = half3(0.f, 0.f, 0.f),
},
.sourceDataspace = kDestDataSpace,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index c9f9ec3..a0bba59 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()});
@@ -1313,7 +1307,7 @@
* produce the insected roundRect. If false, the returned state of the radii param is undefined.
*/
static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
- const SkRect& insetCrop, float cornerRadius,
+ const SkRect& insetCrop, const vec2& cornerRadius,
SkVector radii[4]) {
const bool leftEqual = bounds.fLeft == crop.fLeft;
const bool topEqual = bounds.fTop == crop.fTop;
@@ -1325,8 +1319,8 @@
// In particular the round rect implementation will scale the value of all corner radii
// if the sum of the radius along any edge is greater than the length of that edge.
// See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
- const bool requiredWidth = bounds.width() > (cornerRadius * 2);
- const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+ const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
+ const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
if (!requiredWidth || !requiredHeight) {
return false;
}
@@ -1335,7 +1329,7 @@
// contained within the cropped shape and does not need rounded.
// compute the UpperLeft corner radius
if (leftEqual && topEqual) {
- radii[0].set(cornerRadius, cornerRadius);
+ radii[0].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[0].set(0, 0);
@@ -1344,7 +1338,7 @@
}
// compute the UpperRight corner radius
if (rightEqual && topEqual) {
- radii[1].set(cornerRadius, cornerRadius);
+ radii[1].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fRight <= insetCrop.fRight)) {
radii[1].set(0, 0);
@@ -1353,7 +1347,7 @@
}
// compute the BottomRight corner radius
if (rightEqual && bottomEqual) {
- radii[2].set(cornerRadius, cornerRadius);
+ radii[2].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fRight <= insetCrop.fRight)) {
radii[2].set(0, 0);
@@ -1362,7 +1356,7 @@
}
// compute the BottomLeft corner radius
if (leftEqual && bottomEqual) {
- radii[3].set(cornerRadius, cornerRadius);
+ radii[3].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[3].set(0, 0);
@@ -1375,22 +1369,22 @@
inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
const FloatRect& cropRect,
- const float cornerRadius) {
+ const vec2& cornerRadius) {
const SkRect bounds = getSkRect(boundsRect);
const SkRect crop = getSkRect(cropRect);
SkRRect clip;
- if (cornerRadius > 0) {
+ if (cornerRadius.x > 0 && cornerRadius.y > 0) {
// it the crop and the bounds are equivalent or there is no crop then we don't need a clip
if (bounds == crop || crop.isEmpty()) {
- return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+ return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
}
// This makes an effort to speed up common, simple bounds + clip combinations by
// converting them to a single RRect draw. It is possible there are other cases
// that can be converted.
if (crop.contains(bounds)) {
- const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+ const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
if (insetCrop.contains(bounds)) {
return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
}
@@ -1404,7 +1398,7 @@
}
// we didn't hit any of our fast paths so set the clip to the cropRect
- clip.setRectXY(crop, cornerRadius, cornerRadius);
+ clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
}
// if we hit this point then we either don't have rounded corners or we are going to rely
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index a8bc4f7..34f18c1 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -94,7 +94,8 @@
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
- const FloatRect& crop, float cornerRadius);
+ const FloatRect& crop,
+ const vec2& cornerRadius);
inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 61af698..f289730 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -444,7 +444,9 @@
const ubyte4& backgroundColor) {
const Rect casterRect(castingLayer.geometry.boundaries);
Region casterRegion = Region(casterRect);
- const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ const float casterCornerRadius = (castingLayer.geometry.roundedCornersRadius.x +
+ castingLayer.geometry.roundedCornersRadius.y) /
+ 2.0;
if (casterCornerRadius > 0.0f) {
// ignore the corners if a corner radius is set
Rect cornerRect(casterCornerRadius, casterCornerRadius);
@@ -1129,7 +1131,7 @@
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
- layer.geometry.roundedCornersRadius = 5.0f;
+ layer.geometry.roundedCornersRadius = {5.0f, 5.0f};
layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
@@ -1606,6 +1608,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();
@@ -2131,7 +2163,7 @@
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
- castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
renderengine::ShadowSettings settings =
@@ -2219,7 +2251,8 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
+
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2231,7 +2264,7 @@
renderengine::LayerSettings greenLayer;
greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
greenLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- greenLayer.geometry.roundedCornersRadius = 5.0f;
+ greenLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
// Bottom right corner is not going to be rounded.
greenLayer.geometry.roundedCornersCrop =
Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT,
@@ -2268,7 +2301,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2313,7 +2346,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
- redLayer.geometry.roundedCornersRadius = 64;
+ redLayer.geometry.roundedCornersRadius = {64.0f, 64.0f};
redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2334,6 +2367,49 @@
expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersXY) {
+ if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = {5.0f, 20.0f};
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(redLayer);
+
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+ // Y-axis draws a larger radius, check that its untouched as well
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 5), 0, 0, 0, 0);
+ expectBufferColor(Point(0, 5), 0, 0, 0, 0);
+
+ // middle should be red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
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/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 2ebdbcf..97d57e4 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -240,19 +240,16 @@
return nullptr;
}
-input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map,
- const char* key) {
- String8 keyString(key);
+input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* key) {
if (map != nullptr) {
- if (map->propertyMap->hasProperty(keyString)) {
- auto prop = new input_property_t();
- if (!map->propertyMap->tryGetProperty(keyString, prop->value)) {
- delete prop;
- return nullptr;
- }
- prop->key = keyString;
- return prop;
+ std::string value;
+ auto prop = std::make_unique<input_property_t>();
+ if (!map->propertyMap->tryGetProperty(key, value)) {
+ return nullptr;
}
+ prop->key = key;
+ prop->value = value.c_str();
+ return prop.release();
}
return nullptr;
}
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..a17d2c0 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -502,7 +502,7 @@
bool EventHub::Device::isExternalDeviceLocked() {
if (configuration) {
bool value;
- if (configuration->tryGetProperty(String8("device.internal"), value)) {
+ if (configuration->tryGetProperty("device.internal", value)) {
return !value;
}
}
@@ -512,7 +512,7 @@
bool EventHub::Device::deviceHasMicLocked() {
if (configuration) {
bool value;
- if (configuration->tryGetProperty(String8("audio.mic"), value)) {
+ if (configuration->tryGetProperty("audio.mic", value)) {
return value;
}
}
@@ -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;
}
@@ -2072,10 +2076,9 @@
}
// See if this is a rotary encoder type device.
- String8 deviceType = String8();
- if (device->configuration &&
- device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
- if (!deviceType.compare(String8("rotaryEncoder"))) {
+ std::string deviceType;
+ if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
+ if (deviceType == "rotaryEncoder") {
device->classes |= InputDeviceClass::ROTARY_ENCODER;
}
}
@@ -2413,53 +2416,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..79188aa 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -36,7 +36,6 @@
#include <sys/epoll.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
@@ -44,6 +43,8 @@
#include "TouchVideoDevice.h"
#include "VibrationElement.h"
+struct inotify_event;
+
namespace android {
/* Number of colors : {red, green, blue} */
@@ -648,7 +649,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..baa6007 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,23 +224,22 @@
}
void CursorInputMapper::configureParameters() {
- mParameters.mode = Parameters::MODE_POINTER;
- String8 cursorModeString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
- cursorModeString)) {
+ mParameters.mode = Parameters::Mode::POINTER;
+ std::string cursorModeString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("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());
+ ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.c_str());
}
}
mParameters.orientationAware = false;
- getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
+ getDeviceContext().getConfiguration().tryGetProperty("cursor.orientationAware",
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 +248,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 +474,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/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8eb870f..6a406d2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -160,7 +160,7 @@
static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
int32_t mapped = 0;
- if (config.tryGetProperty(String8(property), mapped) && mapped > 0) {
+ if (config.tryGetProperty(property, mapped) && mapped > 0) {
for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
if (stemKeyRotationMap[i][0] == keyCode) {
stemKeyRotationMap[i][1] = mapped;
@@ -173,7 +173,7 @@
void KeyboardInputMapper::configureParameters() {
mParameters.orientationAware = false;
const PropertyMap& config = getDeviceContext().getConfiguration();
- config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware);
+ config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
if (mParameters.orientationAware) {
mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
@@ -183,10 +183,10 @@
}
mParameters.handlesKeyRepeat = false;
- config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+ config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
mParameters.doNotWakeByDefault = false;
- config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault);
+ config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
}
void KeyboardInputMapper::dumpParameters(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index eca25f6..05973f7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -40,10 +40,10 @@
if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
float res = 0.0f;
- if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.res"), res)) {
+ if (!getDeviceContext().getConfiguration().tryGetProperty("device.res", res)) {
ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
}
- if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.scalingFactor"),
+ if (!getDeviceContext().getConfiguration().tryGetProperty("device.scalingFactor",
mScalingFactor)) {
ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
"default to 1.0!\n");
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index b01c2bc..573f99c 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -64,7 +64,7 @@
template <typename T>
bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) {
const auto& config = getDeviceContext().getConfiguration();
- return config.tryGetProperty(String8(keyName.c_str()), outValue);
+ return config.tryGetProperty(keyName, outValue);
}
void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index d6b72ed..2f3337b 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -415,15 +415,15 @@
? Parameters::GestureMode::SINGLE_TOUCH
: Parameters::GestureMode::MULTI_TOUCH;
- String8 gestureModeString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
+ std::string gestureModeString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("touch.gestureMode",
gestureModeString)) {
if (gestureModeString == "single-touch") {
mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
} else if (gestureModeString == "multi-touch") {
mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
} else if (gestureModeString != "default") {
- ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
+ ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.c_str());
}
}
@@ -445,8 +445,8 @@
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
- String8 deviceTypeString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
+ std::string deviceTypeString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType",
deviceTypeString)) {
if (deviceTypeString == "touchScreen") {
mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
@@ -457,17 +457,17 @@
} else if (deviceTypeString == "pointer") {
mParameters.deviceType = Parameters::DeviceType::POINTER;
} else if (deviceTypeString != "default") {
- ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
+ ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
}
}
mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
- getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+ getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
mParameters.orientationAware);
mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
- String8 orientationString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"),
+ std::string orientationString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
orientationString)) {
if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
@@ -478,7 +478,7 @@
} else if (orientationString == "ORIENTATION_270") {
mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
} else if (orientationString != "ORIENTATION_0") {
- ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string());
+ ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
}
}
@@ -490,8 +490,8 @@
mParameters.hasAssociatedDisplay = true;
if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
- String8 uniqueDisplayId;
- getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
+ std::string uniqueDisplayId;
+ getDeviceContext().getConfiguration().tryGetProperty("touch.displayId",
uniqueDisplayId);
mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
}
@@ -504,7 +504,7 @@
// Normally we don't do this for internal touch screens to prevent them from waking
// up in your pocket but you can enable it using the input device configuration.
mParameters.wake = getDeviceContext().isExternal();
- getDeviceContext().getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
+ getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake);
}
void TouchInputMapper::dumpParameters(std::string& dump) {
@@ -1053,6 +1053,10 @@
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
} else {
+ if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT &&
+ !mConfig.showTouches) {
+ mPointerController->clearSpots();
+ }
mPointerController.reset();
}
@@ -1188,8 +1192,8 @@
// Size
out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
- String8 sizeCalibrationString;
- if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
+ std::string sizeCalibrationString;
+ if (in.tryGetProperty("touch.size.calibration", sizeCalibrationString)) {
if (sizeCalibrationString == "none") {
out.sizeCalibration = Calibration::SizeCalibration::NONE;
} else if (sizeCalibrationString == "geometric") {
@@ -1201,18 +1205,18 @@
} else if (sizeCalibrationString == "area") {
out.sizeCalibration = Calibration::SizeCalibration::AREA;
} else if (sizeCalibrationString != "default") {
- ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
+ ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.c_str());
}
}
- out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale);
- out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias);
- out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
+ out.haveSizeScale = in.tryGetProperty("touch.size.scale", out.sizeScale);
+ out.haveSizeBias = in.tryGetProperty("touch.size.bias", out.sizeBias);
+ out.haveSizeIsSummed = in.tryGetProperty("touch.size.isSummed", out.sizeIsSummed);
// Pressure
out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
- String8 pressureCalibrationString;
- if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
+ std::string pressureCalibrationString;
+ if (in.tryGetProperty("touch.pressure.calibration", pressureCalibrationString)) {
if (pressureCalibrationString == "none") {
out.pressureCalibration = Calibration::PressureCalibration::NONE;
} else if (pressureCalibrationString == "physical") {
@@ -1221,16 +1225,16 @@
out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
} else if (pressureCalibrationString != "default") {
ALOGW("Invalid value for touch.pressure.calibration: '%s'",
- pressureCalibrationString.string());
+ pressureCalibrationString.c_str());
}
}
- out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
+ out.havePressureScale = in.tryGetProperty("touch.pressure.scale", out.pressureScale);
// Orientation
out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
- String8 orientationCalibrationString;
- if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
+ std::string orientationCalibrationString;
+ if (in.tryGetProperty("touch.orientation.calibration", orientationCalibrationString)) {
if (orientationCalibrationString == "none") {
out.orientationCalibration = Calibration::OrientationCalibration::NONE;
} else if (orientationCalibrationString == "interpolated") {
@@ -1239,36 +1243,36 @@
out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
} else if (orientationCalibrationString != "default") {
ALOGW("Invalid value for touch.orientation.calibration: '%s'",
- orientationCalibrationString.string());
+ orientationCalibrationString.c_str());
}
}
// Distance
out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
- String8 distanceCalibrationString;
- if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
+ std::string distanceCalibrationString;
+ if (in.tryGetProperty("touch.distance.calibration", distanceCalibrationString)) {
if (distanceCalibrationString == "none") {
out.distanceCalibration = Calibration::DistanceCalibration::NONE;
} else if (distanceCalibrationString == "scaled") {
out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
} else if (distanceCalibrationString != "default") {
ALOGW("Invalid value for touch.distance.calibration: '%s'",
- distanceCalibrationString.string());
+ distanceCalibrationString.c_str());
}
}
- out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
+ out.haveDistanceScale = in.tryGetProperty("touch.distance.scale", out.distanceScale);
out.coverageCalibration = Calibration::CoverageCalibration::DEFAULT;
- String8 coverageCalibrationString;
- if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
+ std::string coverageCalibrationString;
+ if (in.tryGetProperty("touch.coverage.calibration", coverageCalibrationString)) {
if (coverageCalibrationString == "none") {
out.coverageCalibration = Calibration::CoverageCalibration::NONE;
} else if (coverageCalibrationString == "box") {
out.coverageCalibration = Calibration::CoverageCalibration::BOX;
} else if (coverageCalibrationString != "default") {
ALOGW("Invalid value for touch.coverage.calibration: '%s'",
- coverageCalibrationString.string());
+ coverageCalibrationString.c_str());
}
}
}
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..0fdffdf 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;
};
@@ -546,7 +546,7 @@
enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
}
- void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
+ void addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
Device* device = getDevice(deviceId);
device->configuration.addProperty(key, value);
}
@@ -2731,7 +2731,7 @@
TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
// Configuration.
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value"));
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "key", "value");
FakeInputMapper& mapper1 =
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
@@ -2752,10 +2752,10 @@
InputReaderConfiguration config;
mDevice->configure(ARBITRARY_TIME, &config, 0);
- String8 propertyValue;
- ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue))
+ std::string propertyValue;
+ ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
<< "Device should have read configuration during configuration phase.";
- ASSERT_STREQ("value", propertyValue.string());
+ ASSERT_EQ("value", propertyValue);
ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
@@ -2953,7 +2953,7 @@
}
void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value));
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
}
void configureDevice(uint32_t changes) {
@@ -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/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp
index 1006a06..64a5499 100644
--- a/services/powermanager/WorkSource.cpp
+++ b/services/powermanager/WorkSource.cpp
@@ -28,9 +28,16 @@
return BAD_VALUE;
}
int32_t num;
+ int32_t workChainCount;
status_t ret = parcel->readInt32(&num)
?: parcel->readInt32Vector(&mUids)
- ?: parcel->readString16Vector(&mNames);
+ ?: parcel->readString16Vector(&mNames)
+ ?: parcel->readInt32(&workChainCount);
+
+ if (ret == OK && workChainCount > 0) {
+ // We don't yet support WorkChains in native WorkSources.
+ return BAD_VALUE;
+ }
return ret;
}
@@ -43,7 +50,8 @@
return parcel->writeInt32(mUids.size())
?: parcel->writeInt32Vector(mUids)
- ?: parcel->writeString16Vector(mNames);
+ ?: parcel->writeString16Vector(mNames)
+ ?: parcel->writeInt32(-1);
}
} // namespace android::os
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 2d1558a..962784c 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -31,6 +31,7 @@
"PowerHalWrapperAidlTest.cpp",
"PowerHalWrapperHidlV1_0Test.cpp",
"PowerHalWrapperHidlV1_1Test.cpp",
+ "WorkSourceTest.cpp",
],
cflags: [
"-Wall",
diff --git a/services/powermanager/tests/WorkSourceTest.cpp b/services/powermanager/tests/WorkSourceTest.cpp
new file mode 100644
index 0000000..bb9164a
--- /dev/null
+++ b/services/powermanager/tests/WorkSourceTest.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <optional>
+#define LOG_TAG "PowerHalLoaderTest"
+
+#include <android-base/logging.h>
+#include <android/WorkSource.h>
+#include <binder/Parcel.h>
+#include <gtest/gtest.h>
+
+#include <future>
+
+using namespace android;
+using namespace testing;
+
+TEST(WorkSourceTest, Parcel) {
+ std::vector<int32_t> uids = {1, 2};
+ using Names = std::vector<std::optional<String16>>;
+ std::optional<Names> names = std::make_optional<Names>({std::make_optional(String16("name"))});
+ os::WorkSource ws{uids, names};
+
+ Parcel p;
+ ws.writeToParcel(&p);
+ p.setDataPosition(0);
+
+ os::WorkSource otherWs;
+ otherWs.readFromParcel(&p);
+
+ EXPECT_EQ(ws, otherWs);
+ EXPECT_EQ(uids, otherWs.getUids());
+ EXPECT_EQ(names, otherWs.getNames());
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 1ddf2de..eb17995 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -142,7 +142,6 @@
srcs: [
"BackgroundExecutor.cpp",
"BufferLayer.cpp",
- "BufferLayerConsumer.cpp",
"BufferStateLayer.cpp",
"ClientCache.cpp",
"Client.cpp",
@@ -168,10 +167,8 @@
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerProtoHelper.cpp",
- "LayerRejecter.cpp",
"LayerRenderArea.cpp",
"LayerVector.cpp",
- "MonitoredProducer.cpp",
"NativeWindowSurface.cpp",
"RefreshRateOverlay.cpp",
"RegionSamplingThread.cpp",
@@ -279,7 +276,7 @@
"liblog",
],
static_libs: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
@@ -287,6 +284,6 @@
"libui",
],
export_static_lib_headers: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
}
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index fb15f1d..5d7d293 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -53,7 +53,6 @@
#include "Colorizer.h"
#include "DisplayDevice.h"
#include "FrameTracer/FrameTracer.h"
-#include "LayerRejecter.h"
#include "TimeStats/TimeStats.h"
namespace android {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 3bb0fb3..7cc67a2 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -34,14 +34,12 @@
#include <utils/String8.h>
#include <utils/Timers.h>
-#include "BufferLayerConsumer.h"
#include "Client.h"
#include "DisplayHardware/HWComposer.h"
#include "FrameTimeline.h"
#include "FrameTracker.h"
#include "Layer.h"
#include "LayerVector.h"
-#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
namespace android {
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
deleted file mode 100644
index 7361a4f..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "BufferLayerConsumer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include "BufferLayerConsumer.h"
-#include "Layer.h"
-#include "Scheduler/VsyncController.h"
-
-#include <inttypes.h>
-
-#include <cutils/compiler.h>
-
-#include <hardware/hardware.h>
-
-#include <math/mat4.h>
-
-#include <gui/BufferItem.h>
-#include <gui/GLConsumer.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <renderengine/RenderEngine.h>
-#include <renderengine/impl/ExternalTexture.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// Macros for including the BufferLayerConsumer name in log messages
-#define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
-
-static const mat4 mtxIdentity;
-
-BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,
- renderengine::RenderEngine& engine, uint32_t tex,
- Layer* layer)
- : ConsumerBase(bq, false),
- mCurrentCrop(Rect::EMPTY_RECT),
- mCurrentTransform(0),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mCurrentFence(Fence::NO_FENCE),
- mCurrentTimestamp(0),
- mCurrentDataSpace(ui::Dataspace::UNKNOWN),
- mCurrentFrameNumber(0),
- mCurrentTransformToDisplayInverse(false),
- mCurrentSurfaceDamage(),
- mCurrentApi(0),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mFilteringEnabled(true),
- mRE(engine),
- mTexName(tex),
- mLayer(layer),
- mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
- BLC_LOGV("BufferLayerConsumer");
-
- memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
-
- mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
-}
-
-status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
- Mutex::Autolock lock(mMutex);
- if (mAbandoned) {
- BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!");
- return NO_INIT;
- }
- mDefaultWidth = w;
- mDefaultHeight = h;
- return mConsumer->setDefaultBufferSize(w, h);
-}
-
-void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {
- setFrameAvailableListener(listener);
- Mutex::Autolock lock(mMutex);
- mContentsChangedListener = listener;
-}
-
-status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
- bool* autoRefresh, bool* queuedBuffer,
- uint64_t maxFrameNumber) {
- ATRACE_CALL();
- BLC_LOGV("updateTexImage");
- Mutex::Autolock lock(mMutex);
-
- if (mAbandoned) {
- BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!");
- return NO_INIT;
- }
-
- BufferItem item;
-
- // Acquire the next buffer.
- // In asynchronous mode the list is guaranteed to be one buffer
- // deep, while in synchronous mode we use the oldest buffer.
- status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
- if (err != NO_ERROR) {
- if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- err = NO_ERROR;
- } else if (err == BufferQueue::PRESENT_LATER) {
- // return the error, without logging
- } else {
- BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
- }
- return err;
- }
-
- if (autoRefresh) {
- *autoRefresh = item.mAutoRefresh;
- }
-
- if (queuedBuffer) {
- *queuedBuffer = item.mQueuedBuffer;
- }
-
- // We call the rejecter here, in case the caller has a reason to
- // not accept this buffer. This is used by SurfaceFlinger to
- // reject buffers which have the wrong size
- int slot = item.mSlot;
- if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
- releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
- return BUFFER_REJECTED;
- }
-
- // Release the previous buffer.
- err = updateAndReleaseLocked(item, &mPendingRelease);
- if (err != NO_ERROR) {
- return err;
- }
- return err;
-}
-
-void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
- if (!fence->isValid()) {
- return;
- }
-
- auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture;
- if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
- return;
- }
-
- auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
- : mCurrentTextureBuffer->getBuffer();
- auto err = addReleaseFence(slot, buffer, fence);
- if (err != OK) {
- BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
- }
-}
-
-bool BufferLayerConsumer::releasePendingBuffer() {
- if (!mPendingRelease.isPending) {
- BLC_LOGV("Pending buffer already released");
- return false;
- }
- BLC_LOGV("Releasing pending buffer");
- Mutex::Autolock lock(mMutex);
- status_t result =
- releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer);
- if (result < NO_ERROR) {
- BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result);
- }
- mPendingRelease = PendingRelease();
- return true;
-}
-
-sp<Fence> BufferLayerConsumer::getPrevFinalReleaseFence() const {
- Mutex::Autolock lock(mMutex);
- return ConsumerBase::mPrevFinalReleaseFence;
-}
-
-status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
- uint64_t maxFrameNumber) {
- status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
- if (err != NO_ERROR) {
- return err;
- }
-
- // If item->mGraphicBuffer is not null, this buffer has not been acquired
- // before, so we need to clean up old references.
- if (item->mGraphicBuffer != nullptr) {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
- mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
- mImages[item->mSlot] = std::make_shared<
- renderengine::impl::ExternalTexture>(item->mGraphicBuffer, mRE,
- renderengine::impl::ExternalTexture::
- Usage::READABLE);
- }
- }
-
- return NO_ERROR;
-}
-
-status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
- PendingRelease* pendingRelease) {
- status_t err = NO_ERROR;
-
- int slot = item.mSlot;
-
- BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
- (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr)
- ? mCurrentTextureBuffer->getBuffer()->handle
- : 0,
- slot, mSlots[slot].mGraphicBuffer->handle);
-
- // Hang onto the pointer so that it isn't freed in the call to
- // releaseBufferLocked() if we're in shared buffer mode and both buffers are
- // the same.
-
- std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer;
- {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- nextTextureBuffer = mImages[slot];
- }
-
- // release old buffer
- if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- if (pendingRelease == nullptr) {
- status_t status =
- releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer());
- if (status < NO_ERROR) {
- BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
- status);
- err = status;
- // keep going, with error raised [?]
- }
- } else {
- pendingRelease->currentTexture = mCurrentTexture;
- pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer();
- pendingRelease->isPending = true;
- }
- }
-
- // Update the BufferLayerConsumer state.
- mCurrentTexture = slot;
- mCurrentTextureBuffer = nextTextureBuffer;
- mCurrentCrop = item.mCrop;
- mCurrentTransform = item.mTransform;
- mCurrentScalingMode = item.mScalingMode;
- mCurrentTimestamp = item.mTimestamp;
- mCurrentDataSpace = static_cast<ui::Dataspace>(item.mDataSpace);
- mCurrentHdrMetadata = item.mHdrMetadata;
- mCurrentFence = item.mFence;
- mCurrentFenceTime = item.mFenceTime;
- mCurrentFrameNumber = item.mFrameNumber;
- mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;
- mCurrentSurfaceDamage = item.mSurfaceDamage;
- mCurrentApi = item.mApi;
-
- computeCurrentTransformMatrixLocked();
-
- return err;
-}
-
-void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
- Mutex::Autolock lock(mMutex);
- memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
-}
-
-void BufferLayerConsumer::setFilteringEnabled(bool enabled) {
- Mutex::Autolock lock(mMutex);
- if (mAbandoned) {
- BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!");
- return;
- }
- bool needsRecompute = mFilteringEnabled != enabled;
- mFilteringEnabled = enabled;
-
- if (needsRecompute && mCurrentTextureBuffer == nullptr) {
- BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
- }
-
- if (needsRecompute && mCurrentTextureBuffer != nullptr) {
- computeCurrentTransformMatrixLocked();
- }
-}
-
-void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
- BLC_LOGV("computeCurrentTransformMatrixLocked");
- if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) {
- BLC_LOGD("computeCurrentTransformMatrixLocked: "
- "mCurrentTextureBuffer is nullptr");
- }
- GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
- mCurrentTextureBuffer == nullptr
- ? nullptr
- : mCurrentTextureBuffer->getBuffer(),
- getCurrentCropLocked(), mCurrentTransform,
- mFilteringEnabled);
-}
-
-nsecs_t BufferLayerConsumer::getTimestamp() {
- BLC_LOGV("getTimestamp");
- Mutex::Autolock lock(mMutex);
- return mCurrentTimestamp;
-}
-
-ui::Dataspace BufferLayerConsumer::getCurrentDataSpace() {
- BLC_LOGV("getCurrentDataSpace");
- Mutex::Autolock lock(mMutex);
- return mCurrentDataSpace;
-}
-
-const HdrMetadata& BufferLayerConsumer::getCurrentHdrMetadata() const {
- BLC_LOGV("getCurrentHdrMetadata");
- Mutex::Autolock lock(mMutex);
- return mCurrentHdrMetadata;
-}
-
-uint64_t BufferLayerConsumer::getFrameNumber() {
- BLC_LOGV("getFrameNumber");
- Mutex::Autolock lock(mMutex);
- return mCurrentFrameNumber;
-}
-
-bool BufferLayerConsumer::getTransformToDisplayInverse() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentTransformToDisplayInverse;
-}
-
-const Region& BufferLayerConsumer::getSurfaceDamage() const {
- return mCurrentSurfaceDamage;
-}
-
-void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) {
- if (damage.bounds() == Rect::INVALID_RECT ||
- mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) {
- mCurrentSurfaceDamage = Region::INVALID_REGION;
- } else {
- mCurrentSurfaceDamage |= damage;
- }
-}
-
-int BufferLayerConsumer::getCurrentApi() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentApi;
-}
-
-std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer(
- int* outSlot, sp<Fence>* outFence) const {
- Mutex::Autolock lock(mMutex);
-
- if (outSlot != nullptr) {
- *outSlot = mCurrentTexture;
- }
-
- if (outFence != nullptr) {
- *outFence = mCurrentFence;
- }
-
- return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer;
-}
-
-Rect BufferLayerConsumer::getCurrentCrop() const {
- Mutex::Autolock lock(mMutex);
- return getCurrentCropLocked();
-}
-
-Rect BufferLayerConsumer::getCurrentCropLocked() const {
- uint32_t width = mDefaultWidth;
- uint32_t height = mDefaultHeight;
- // If the buffer comes with a rotated bit for 90 (or 270) degrees, switch width/height in order
- // to scale and crop correctly.
- if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- width = mDefaultHeight;
- height = mDefaultWidth;
- }
-
- return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
- ? GLConsumer::scaleDownCrop(mCurrentCrop, width, height)
- : mCurrentCrop;
-}
-
-uint32_t BufferLayerConsumer::getCurrentTransform() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentTransform;
-}
-
-uint32_t BufferLayerConsumer::getCurrentScalingMode() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentScalingMode;
-}
-
-sp<Fence> BufferLayerConsumer::getCurrentFence() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentFence;
-}
-
-std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentFenceTime;
-}
-
-void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
- BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
- std::lock_guard<std::mutex> lock(mImagesMutex);
- if (slotIndex == mCurrentTexture) {
- mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
- }
- mImages[slotIndex] = nullptr;
- ConsumerBase::freeBufferLocked(slotIndex);
-}
-
-void BufferLayerConsumer::onDisconnect() {
- Mutex::Autolock lock(mMutex);
-
- if (mAbandoned) {
- // Nothing to do if we're already abandoned.
- return;
- }
-
- mLayer->onDisconnect();
-}
-
-void BufferLayerConsumer::onSidebandStreamChanged() {
- [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
- {
- Mutex::Autolock lock(mFrameAvailableMutex);
- unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
- }
- sp<ContentsChangedListener> listener;
- { // scope for the lock
- Mutex::Autolock lock(mMutex);
- ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
- listener = mContentsChangedListener.promote();
- }
-
- if (listener != nullptr) {
- listener->onSidebandStreamChanged();
- }
-}
-
-void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
- if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot];
- if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
- oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
- mImages[item.mSlot] = std::make_shared<
- renderengine::impl::ExternalTexture>(item.mGraphicBuffer, mRE,
- renderengine::impl::ExternalTexture::
- Usage::READABLE);
- }
- }
-}
-
-void BufferLayerConsumer::abandonLocked() {
- BLC_LOGV("abandonLocked");
- mCurrentTextureBuffer = nullptr;
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- mImages[i] = nullptr;
- }
- ConsumerBase::abandonLocked();
-}
-
-status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) {
- return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
-}
-
-void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const {
- result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
- "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
- prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
- mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
- mCurrentTransform);
-
- ConsumerBase::dumpLocked(result, prefix);
-}
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
deleted file mode 100644
index 23ad2a3..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef ANDROID_BUFFERLAYERCONSUMER_H
-#define ANDROID_BUFFERLAYERCONSUMER_H
-
-#include <android-base/thread_annotations.h>
-#include <gui/BufferQueueDefs.h>
-#include <gui/ConsumerBase.h>
-#include <gui/HdrMetadata.h>
-#include <renderengine/ExternalTexture.h>
-#include <ui/FenceTime.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
-#include <ui/Region.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Layer;
-class String8;
-
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
-/*
- * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue,
- * and makes them available to RenderEngine as a texture.
- *
- * A typical usage pattern is to call updateTexImage() when a new frame is
- * desired. If a new frame is available, the frame is latched. If not, the
- * previous contents are retained. The texture is attached and updated after
- * bindTextureImage() is called.
- *
- * All calls to updateTexImage must be made with RenderEngine being current.
- * The texture is attached to the TEXTURE_EXTERNAL texture target.
- */
-class BufferLayerConsumer : public ConsumerBase {
-public:
- static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
-
- class BufferRejecter {
- friend class BufferLayerConsumer;
- virtual bool reject(const sp<GraphicBuffer>& buf, const BufferItem& item) = 0;
-
- protected:
- virtual ~BufferRejecter() {}
- };
-
- struct ContentsChangedListener : public FrameAvailableListener {
- virtual void onSidebandStreamChanged() = 0;
- };
-
- // BufferLayerConsumer constructs a new BufferLayerConsumer object. The
- // tex parameter indicates the name of the RenderEngine texture to which
- // images are to be streamed.
- BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine,
- uint32_t tex, Layer* layer);
-
- // Sets the contents changed listener. This should be used instead of
- // ConsumerBase::setFrameAvailableListener().
- void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
-
- // updateTexImage acquires the most recently queued buffer, and sets the
- // image contents of the target texture to it.
- //
- // This call may only be made while RenderEngine is current.
- //
- // This calls doFenceWait to ensure proper synchronization unless native
- // fence is supported.
- //
- // Unlike the GLConsumer version, this version takes a functor that may be
- // used to reject the newly acquired buffer. It also does not bind the
- // RenderEngine texture until bindTextureImage is called.
- status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
- bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
-
- // setReleaseFence stores a fence that will signal when the current buffer
- // is no longer being read. This fence will be returned to the producer
- // when the current buffer is released by updateTexImage(). Multiple
- // fences can be set for a given buffer; they will be merged into a single
- // union fence.
- void setReleaseFence(const sp<Fence>& fence);
-
- bool releasePendingBuffer();
-
- sp<Fence> getPrevFinalReleaseFence() const;
-
- // See GLConsumer::getTransformMatrix.
- void getTransformMatrix(float mtx[16]);
-
- // getTimestamp retrieves the timestamp associated with the texture image
- // set by the most recent call to updateTexImage.
- //
- // The timestamp is in nanoseconds, and is monotonically increasing. Its
- // other semantics (zero point, etc) are source-dependent and should be
- // documented by the source.
- int64_t getTimestamp();
-
- // getDataSpace retrieves the DataSpace associated with the texture image
- // set by the most recent call to updateTexImage.
- ui::Dataspace getCurrentDataSpace();
-
- // getCurrentHdrMetadata retrieves the HDR metadata associated with the
- // texture image set by the most recent call to updateTexImage.
- const HdrMetadata& getCurrentHdrMetadata() const;
-
- // getFrameNumber retrieves the frame number associated with the texture
- // image set by the most recent call to updateTexImage.
- //
- // The frame number is an incrementing counter set to 0 at the creation of
- // the BufferQueue associated with this consumer.
- uint64_t getFrameNumber();
-
- bool getTransformToDisplayInverse() const;
-
- // must be called from SF main thread
- const Region& getSurfaceDamage() const;
-
- // Merge the given damage region into the current damage region value.
- void mergeSurfaceDamage(const Region& damage);
-
- // getCurrentApi retrieves the API which queues the current buffer.
- int getCurrentApi() const;
-
- // See GLConsumer::setDefaultBufferSize.
- status_t setDefaultBufferSize(uint32_t width, uint32_t height);
-
- // setFilteringEnabled sets whether the transform matrix should be computed
- // for use with bilinear filtering.
- void setFilteringEnabled(bool enabled);
-
- // getCurrentBuffer returns the buffer associated with the current image.
- // When outSlot is not nullptr, the current buffer slot index is also
- // returned. Simiarly, when outFence is not nullptr, the current output
- // fence is returned.
- std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer(
- int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
-
- // getCurrentCrop returns the cropping rectangle of the current buffer.
- Rect getCurrentCrop() const;
-
- // getCurrentTransform returns the transform of the current buffer.
- uint32_t getCurrentTransform() const;
-
- // getCurrentScalingMode returns the scaling mode of the current buffer.
- uint32_t getCurrentScalingMode() const;
-
- // getCurrentFence returns the fence indicating when the current buffer is
- // ready to be read from.
- sp<Fence> getCurrentFence() const;
-
- // getCurrentFence returns the FenceTime indicating when the current
- // buffer is ready to be read from.
- std::shared_ptr<FenceTime> getCurrentFenceTime() const;
-
- // setConsumerUsageBits overrides the ConsumerBase method to OR
- // DEFAULT_USAGE_FLAGS to usage.
- status_t setConsumerUsageBits(uint64_t usage);
- void onBufferAvailable(const BufferItem& item) EXCLUDES(mImagesMutex);
-
-protected:
- // abandonLocked overrides the ConsumerBase method to clear
- // mCurrentTextureImage in addition to the ConsumerBase behavior.
- virtual void abandonLocked() EXCLUDES(mImagesMutex);
-
- // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
- // specific info in addition to the ConsumerBase behavior.
- virtual void dumpLocked(String8& result, const char* prefix) const;
-
- // See ConsumerBase::acquireBufferLocked
- virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) override
- EXCLUDES(mImagesMutex);
-
- bool canUseImageCrop(const Rect& crop) const;
-
- struct PendingRelease {
- PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}
-
- bool isPending;
- int currentTexture;
- sp<GraphicBuffer> graphicBuffer;
- };
-
- // This releases the buffer in the slot referenced by mCurrentTexture,
- // then updates state to refer to the BufferItem, which must be a
- // newly-acquired buffer. If pendingRelease is not null, the parameters
- // which would have been passed to releaseBufferLocked upon the successful
- // completion of the method will instead be returned to the caller, so that
- // it may call releaseBufferLocked itself later.
- status_t updateAndReleaseLocked(const BufferItem& item,
- PendingRelease* pendingRelease = nullptr)
- EXCLUDES(mImagesMutex);
-
-private:
- // Utility class for managing GraphicBuffer references into renderengine
- class Image {
- public:
- Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine);
- virtual ~Image();
- const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
-
- private:
- // mGraphicBuffer is the buffer that was used to create this image.
- sp<GraphicBuffer> mGraphicBuffer;
- // Back-reference into renderengine to initiate cleanup.
- renderengine::RenderEngine& mRE;
- DISALLOW_COPY_AND_ASSIGN(Image);
- };
-
- // freeBufferLocked frees up the given buffer slot. If the slot has been
- // initialized this will release the reference to the GraphicBuffer in
- // that slot. Otherwise it has no effect.
- //
- // This method must be called with mMutex locked.
- virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);
-
- // IConsumerListener interface
- void onDisconnect() override;
- void onSidebandStreamChanged() override;
- void addAndGetFrameTimestamps(const NewFrameEventsEntry*, FrameEventHistoryDelta*) override {}
-
- // computeCurrentTransformMatrixLocked computes the transform matrix for the
- // current texture. It uses mCurrentTransform and the current GraphicBuffer
- // to compute this matrix and stores it in mCurrentTransformMatrix.
- // mCurrentTextureImage must not be nullptr.
- void computeCurrentTransformMatrixLocked();
-
- // getCurrentCropLocked returns the cropping rectangle of the current buffer.
- Rect getCurrentCropLocked() const;
-
- // The default consumer usage flags that BufferLayerConsumer always sets on its
- // BufferQueue instance; these will be OR:d with any additional flags passed
- // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always
- // consume buffers as hardware textures.
- static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
-
- // mCurrentTextureBuffer is the buffer containing the current texture. It's
- // possible that this buffer is not associated with any buffer slot, so we
- // must track it separately in order to support the getCurrentBuffer method.
- std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer;
-
- // mCurrentCrop is the crop rectangle that applies to the current texture.
- // It gets set each time updateTexImage is called.
- Rect mCurrentCrop;
-
- // mCurrentTransform is the transform identifier for the current texture. It
- // gets set each time updateTexImage is called.
- uint32_t mCurrentTransform;
-
- // mCurrentScalingMode is the scaling mode for the current texture. It gets
- // set each time updateTexImage is called.
- uint32_t mCurrentScalingMode;
-
- // mCurrentFence is the fence received from BufferQueue in updateTexImage.
- sp<Fence> mCurrentFence;
-
- // The FenceTime wrapper around mCurrentFence.
- std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
-
- // mCurrentTransformMatrix is the transform matrix for the current texture.
- // It gets computed by computeTransformMatrix each time updateTexImage is
- // called.
- float mCurrentTransformMatrix[16];
-
- // mCurrentTimestamp is the timestamp for the current texture. It
- // gets set each time updateTexImage is called.
- int64_t mCurrentTimestamp;
-
- // mCurrentDataSpace is the dataspace for the current texture. It
- // gets set each time updateTexImage is called.
- ui::Dataspace mCurrentDataSpace;
-
- // mCurrentHdrMetadata is the HDR metadata for the current texture. It
- // gets set each time updateTexImage is called.
- HdrMetadata mCurrentHdrMetadata;
-
- // mCurrentFrameNumber is the frame counter for the current texture.
- // It gets set each time updateTexImage is called.
- uint64_t mCurrentFrameNumber;
-
- // Indicates this buffer must be transformed by the inverse transform of the screen
- // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform.
- // This must be set/read from SurfaceFlinger's main thread.
- bool mCurrentTransformToDisplayInverse;
-
- // The portion of this surface that has changed since the previous frame
- Region mCurrentSurfaceDamage;
-
- int mCurrentApi;
-
- uint32_t mDefaultWidth, mDefaultHeight;
-
- // mFilteringEnabled indicates whether the transform matrix is computed for
- // use with bilinear filtering. It defaults to true and is changed by
- // setFilteringEnabled().
- bool mFilteringEnabled;
-
- renderengine::RenderEngine& mRE;
-
- // mTexName is the name of the RenderEngine texture to which streamed
- // images will be bound when bindTexImage is called. It is set at
- // construction time.
- const uint32_t mTexName;
-
- // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
- Layer* mLayer GUARDED_BY(mMutex);
-
- wp<ContentsChangedListener> mContentsChangedListener;
-
- // mCurrentTexture is the buffer slot index of the buffer that is currently
- // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
- // indicating that no buffer slot is currently bound to the texture. Note,
- // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
- // that no buffer is bound to the texture. A call to setBufferCount will
- // reset mCurrentTexture to INVALID_BUFFER_SLOT.
- int mCurrentTexture;
-
- // Shadow buffer cache for cleaning up renderengine references.
- std::shared_ptr<renderengine::ExternalTexture>
- mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
-
- // Separate mutex guarding the shadow buffer cache.
- // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
- // which is contentious enough that we can't just use mMutex.
- mutable std::mutex mImagesMutex;
-
- // A release that is pending on the receipt of a new release fence from
- // presentDisplay
- PendingRelease mPendingRelease;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_BUFFERLAYERCONSUMER_H
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..16cf903 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -70,8 +70,6 @@
#include "FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "LayerProtoHelper.h"
-#include "LayerRejecter.h"
-#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
@@ -148,6 +146,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.
@@ -409,7 +408,7 @@
const auto& drawingState{getDrawingState()};
const auto alpha = static_cast<float>(getAlpha());
const bool opaque = isOpaque(drawingState);
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
if (!opaque || alpha != 1.0f) {
@@ -485,7 +484,7 @@
compositionState->hasProtectedContent = isProtected();
compositionState->dimmingEnabled = isDimmingEnabled();
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
compositionState->isOpaque =
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
@@ -724,7 +723,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 +731,10 @@
mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering();
}
+ if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) {
+ mFlinger->mUpdateInputInfo = true;
+ }
+
commitTransaction(mDrawingState);
return flags;
@@ -882,7 +885,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 +982,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 +1100,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;
};
@@ -1943,27 +1966,22 @@
const auto& parent = mDrawingParent.promote();
if (parent != nullptr) {
parentSettings = parent->getRoundedCornerState();
- if (parentSettings.radius > 0) {
+ if (parentSettings.hasRoundedCorners()) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
parentSettings.cropRect = t.transform(parentSettings.cropRect);
- // The rounded corners shader only accepts 1 corner radius for performance reasons,
- // but a transform matrix can define horizontal and vertical scales.
- // Let's take the average between both of them and pass into the shader, practically we
- // never do this type of transformation on windows anyway.
- auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
- auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
- parentSettings.radius *= (scaleX + scaleY) / 2.0f;
+ parentSettings.radius.x *= t.getScaleX();
+ parentSettings.radius.y *= t.getScaleY();
}
}
// Get layer settings
Rect layerCropRect = getCroppedBufferSize(getDrawingState());
- const float radius = getDrawingState().cornerRadius;
+ const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
- const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
+ const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
- if (layerSettingsValid && parentSettings.radius > 0) {
+ if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
// If the parent and the layer have rounded corner settings, use the parent settings if the
// parent crop is entirely inside the layer crop.
// This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
@@ -1977,7 +1995,7 @@
}
} else if (layerSettingsValid) {
return layerSettings;
- } else if (parentSettings.radius > 0) {
+ } else if (parentSettings.hasRoundedCorners()) {
return parentSettings;
}
return {};
@@ -2053,7 +2071,7 @@
mDrawingState.inputInfo = info;
mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
mDrawingState.modified = true;
- mFlinger->mInputInfoChanged = true;
+ mFlinger->mUpdateInputInfo = true;
setTransactionFlags(eTransactionNeeded);
}
@@ -2098,7 +2116,8 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_corner_radius(
+ (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
layerInfo->set_is_trusted_overlay(isTrustedOverlay());
LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
@@ -2632,6 +2651,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..b809c8a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -53,7 +53,6 @@
#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
#include "LayerVector.h"
-#include "MonitoredProducer.h"
#include "RenderArea.h"
#include "Scheduler/LayerInfo.h"
#include "SurfaceFlinger.h"
@@ -137,13 +136,14 @@
struct RoundedCornerState {
RoundedCornerState() = default;
- RoundedCornerState(FloatRect cropRect, float radius)
+ RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
: cropRect(cropRect), radius(radius) {}
// Rounded rectangle in local layer coordinate space.
FloatRect cropRect = FloatRect();
// Radius of the rounded rectangle.
- float radius = 0.0f;
+ vec2 radius;
+ bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
};
using FrameRate = scheduler::LayerInfo::FrameRate;
@@ -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;
@@ -599,7 +606,7 @@
// corner crop does not intersect with its own rounded corner crop.
virtual RoundedCornerState getRoundedCornerState() const;
- bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+ bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
/**
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
deleted file mode 100644
index 1c0263b..0000000
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "LayerRejecter.h"
-
-#include <gui/BufferItem.h>
-#include <system/window.h>
-
-#define DEBUG_RESIZE 0
-
-namespace android {
-
-LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
- bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
- bool transformToDisplayInverse)
- : mFront(front),
- mCurrent(current),
- mRecomputeVisibleRegions(recomputeVisibleRegions),
- mStickyTransformSet(stickySet),
- mName(name),
- mTransformToDisplayInverse(transformToDisplayInverse) {}
-
-bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
- if (buf == nullptr) {
- return false;
- }
-
- uint32_t bufWidth = buf->getWidth();
- uint32_t bufHeight = buf->getHeight();
-
- // check that we received a buffer of the right size
- // (Take the buffer's orientation into account)
- if (item.mTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
-
- if (mTransformToDisplayInverse) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
- }
-
- int actualScalingMode = item.mScalingMode;
- bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
- if (mFront.active_legacy != mFront.requested_legacy) {
- if (isFixedSize ||
- (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) {
- // Here we pretend the transaction happened by updating the
- // current and drawing states. Drawing state is only accessed
- // in this thread, no need to have it locked
- mFront.active_legacy = mFront.requested_legacy;
-
- // We also need to update the current state so that
- // we don't end-up overwriting the drawing state with
- // this stale current state during the next transaction
- //
- // NOTE: We don't need to hold the transaction lock here
- // because State::active_legacy is only accessed from this thread.
- mCurrent.active_legacy = mFront.active_legacy;
- mCurrent.modified = true;
-
- // recompute visible region
- mRecomputeVisibleRegions = true;
-
- if (mFront.crop != mFront.requestedCrop) {
- mFront.crop = mFront.requestedCrop;
- mCurrent.crop = mFront.requestedCrop;
- mRecomputeVisibleRegions = true;
- }
- }
-
- ALOGD_IF(DEBUG_RESIZE,
- "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
- " drawing={ active_legacy ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
- "(%4d,%4d) "
- "}\n"
- " requested_legacy={ wh={%4u,%4u} }}\n",
- mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
- mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
- mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
- mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
- }
-
- if (!isFixedSize && !mStickyTransformSet) {
- if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) {
- // reject this buffer
- ALOGE("[%s] rejecting buffer: "
- "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
- mName.c_str(), bufWidth, bufHeight, mFront.active_legacy.w,
- mFront.active_legacy.h);
- return true;
- }
- }
-
- // if the transparent region has changed (this test is
- // conservative, but that's fine, worst case we're doing
- // a bit of extra work), we latch the new one and we
- // trigger a visible-region recompute.
- //
- // We latch the transparent region here, instead of above where we latch
- // the rest of the geometry because it is only content but not necessarily
- // resize dependent.
- if (!mFront.activeTransparentRegion_legacy.hasSameRects(
- mFront.requestedTransparentRegion_legacy)) {
- mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
-
- // We also need to update the current state so that
- // we don't end-up overwriting the drawing state with
- // this stale current state during the next transaction
- //
- // NOTE: We don't need to hold the transaction lock here
- // because State::active_legacy is only accessed from this thread.
- mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy;
-
- // recompute visible region
- mRecomputeVisibleRegions = true;
- }
-
- return false;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
deleted file mode 100644
index 4981f45..0000000
--- a/services/surfaceflinger/LayerRejecter.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 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 "Layer.h"
-#include "BufferLayerConsumer.h"
-
-namespace android {
-
-class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
-public:
- LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
- bool stickySet, const std::string& name,
- bool transformToDisplayInverse);
-
- virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
-
-private:
- Layer::State& mFront;
- Layer::State& mCurrent;
- bool& mRecomputeVisibleRegions;
- const bool mStickyTransformSet;
- const std::string& mName;
- const bool mTransformToDisplayInverse;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
deleted file mode 100644
index df76f50..0000000
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2014 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "MonitoredProducer.h"
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-
-#include "Scheduler/MessageQueue.h"
-
-namespace android {
-
-MonitoredProducer::MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
- const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer) :
- mProducer(producer),
- mFlinger(flinger),
- mLayer(layer) {}
-
-MonitoredProducer::~MonitoredProducer() {}
-
-status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- return mProducer->requestBuffer(slot, buf);
-}
-
-status_t MonitoredProducer::setMaxDequeuedBufferCount(
- int maxDequeuedBuffers) {
- return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
-}
-
-status_t MonitoredProducer::setAsyncMode(bool async) {
- return mProducer->setAsyncMode(async);
-}
-
-status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
- PixelFormat format, uint64_t usage,
- uint64_t* outBufferAge,
- FrameEventHistoryDelta* outTimestamps) {
- return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
-}
-
-status_t MonitoredProducer::detachBuffer(int slot) {
- return mProducer->detachBuffer(slot);
-}
-
-status_t MonitoredProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) {
- return mProducer->detachNextBuffer(outBuffer, outFence);
-}
-
-status_t MonitoredProducer::attachBuffer(int* outSlot,
- const sp<GraphicBuffer>& buffer) {
- return mProducer->attachBuffer(outSlot, buffer);
-}
-
-status_t MonitoredProducer::queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output) {
- return mProducer->queueBuffer(slot, input, output);
-}
-
-status_t MonitoredProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
- return mProducer->cancelBuffer(slot, fence);
-}
-
-int MonitoredProducer::query(int what, int* value) {
- return mProducer->query(what, value);
-}
-
-status_t MonitoredProducer::connect(const sp<IProducerListener>& listener,
- int api, bool producerControlledByApp, QueueBufferOutput* output) {
- return mProducer->connect(listener, api, producerControlledByApp, output);
-}
-
-status_t MonitoredProducer::disconnect(int api, DisconnectMode mode) {
- return mProducer->disconnect(api, mode);
-}
-
-status_t MonitoredProducer::setSidebandStream(const sp<NativeHandle>& stream) {
- return mProducer->setSidebandStream(stream);
-}
-
-void MonitoredProducer::allocateBuffers(uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage) {
- mProducer->allocateBuffers(width, height, format, usage);
-}
-
-status_t MonitoredProducer::allowAllocation(bool allow) {
- return mProducer->allowAllocation(allow);
-}
-
-status_t MonitoredProducer::setGenerationNumber(uint32_t generationNumber) {
- return mProducer->setGenerationNumber(generationNumber);
-}
-
-String8 MonitoredProducer::getConsumerName() const {
- return mProducer->getConsumerName();
-}
-
-status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) {
- return mProducer->setSharedBufferMode(sharedBufferMode);
-}
-
-status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) {
- return mProducer->setAutoRefresh(autoRefresh);
-}
-
-status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
- return mProducer->setDequeueTimeout(timeout);
-}
-
-status_t MonitoredProducer::setLegacyBufferDrop(bool drop) {
- return mProducer->setLegacyBufferDrop(drop);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence, float outTransformMatrix[16]) {
- return mProducer->getLastQueuedBuffer(outBuffer, outFence,
- outTransformMatrix);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
- Rect* outRect, uint32_t* outTransform) {
- return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
-}
-
-void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
- mProducer->getFrameTimestamps(outDelta);
-}
-
-status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
- return mProducer->getUniqueId(outId);
-}
-
-status_t MonitoredProducer::getConsumerUsage(uint64_t* outUsage) const {
- return mProducer->getConsumerUsage(outUsage);
-}
-
-status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) {
- return mProducer->setAutoPrerotation(autoPrerotation);
-}
-
-IBinder* MonitoredProducer::onAsBinder() {
- return this;
-}
-
-sp<Layer> MonitoredProducer::getLayer() const {
- return mLayer.promote();
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
deleted file mode 100644
index 3778277..0000000
--- a/services/surfaceflinger/MonitoredProducer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2014 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.
- */
-
-#ifndef ANDROID_MONITORED_PRODUCER_H
-#define ANDROID_MONITORED_PRODUCER_H
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-class IProducerListener;
-class NativeHandle;
-class SurfaceFlinger;
-class Layer;
-
-// MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will
-// be notified upon its destruction
-class MonitoredProducer : public BnGraphicBufferProducer {
-public:
- MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
- const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer);
- virtual ~MonitoredProducer();
-
- // From IGraphicBufferProducer
- virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
- virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
- virtual status_t setAsyncMode(bool async);
- virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
- PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
- FrameEventHistoryDelta* outTimestamps);
- virtual status_t detachBuffer(int slot);
- virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
- virtual status_t attachBuffer(int* outSlot,
- const sp<GraphicBuffer>& buffer);
- virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output);
- virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);
- virtual int query(int what, int* value);
- virtual status_t connect(const sp<IProducerListener>& token, int api,
- bool producerControlledByApp, QueueBufferOutput* output);
- virtual status_t disconnect(int api, DisconnectMode mode);
- virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
- virtual void allocateBuffers(uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage);
- virtual status_t allowAllocation(bool allow);
- virtual status_t setGenerationNumber(uint32_t generationNumber);
- virtual String8 getConsumerName() const override;
- virtual status_t setDequeueTimeout(nsecs_t timeout) override;
- virtual status_t setLegacyBufferDrop(bool drop) override;
- virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence, float outTransformMatrix[16]) override;
- virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
- Rect* outRect, uint32_t* outTransform) override;
- virtual IBinder* onAsBinder();
- virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
- virtual status_t setAutoRefresh(bool autoRefresh) override;
- virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
- virtual status_t getUniqueId(uint64_t* outId) const override;
- virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
- virtual status_t setAutoPrerotation(bool autoPrerotation) override;
-
- // The Layer which created this producer, and on which queued Buffer's will be displayed.
- sp<Layer> getLayer() const;
-
-private:
- sp<IGraphicBufferProducer> mProducer;
- sp<SurfaceFlinger> mFlinger;
- // The Layer which created this producer, and on which queued Buffer's will be displayed.
- wp<Layer> mLayer;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MONITORED_PRODUCER_H
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d4435c2..a9180d4 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,9 +16,10 @@
#include <algorithm>
-#include "RefreshRateOverlay.h"
+#include "BackgroundExecutor.h"
#include "Client.h"
#include "Layer.h"
+#include "RefreshRateOverlay.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -56,6 +57,14 @@
} // namespace
+SurfaceControlHolder::~SurfaceControlHolder() {
+ // Hand the sp<SurfaceControl> to the helper thread to release the last
+ // reference. This makes sure that the SurfaceControl is destructed without
+ // SurfaceFlinger::mStateLock held.
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+}
+
void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
SkCanvas& canvas) {
const SkRect rect = [&]() {
@@ -210,21 +219,27 @@
return buffers;
}
+std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
+ sp<SurfaceControl> surfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+}
+
RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
: mFpsRange(fpsRange),
mShowSpinner(showSpinner),
- mSurfaceControl(SurfaceComposerClient::getDefault()
- ->createSurface(String8("RefreshRateOverlay"), kBufferWidth,
- kBufferHeight, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState)) {
+ mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
}
- createTransaction(mSurfaceControl)
- .setLayer(mSurfaceControl, INT32_MAX - 2)
- .setTrustedOverlay(mSurfaceControl, true)
+ createTransaction(mSurfaceControl->get())
+ .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
}
@@ -233,7 +248,7 @@
if (!mSurfaceControl) return kNoBuffers;
const auto transformHint =
- static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
// Tell SurfaceFlinger about the pre-rotation on the buffer.
const auto transform = [&] {
@@ -247,7 +262,9 @@
}
}();
- createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply();
+ createTransaction(mSurfaceControl->get())
+ .setTransform(mSurfaceControl->get(), transform)
+ .apply();
BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
@@ -289,21 +306,21 @@
Rect frame((3 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
- createTransaction(mSurfaceControl)
- .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0,
- frame.getHeight() / static_cast<float>(kBufferHeight))
- .setPosition(mSurfaceControl, frame.left, frame.top)
+ createTransaction(mSurfaceControl->get())
+ .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+ 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+ .setPosition(mSurfaceControl->get(), frame.left, frame.top)
.apply();
}
void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
- createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply();
+ createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
void RefreshRateOverlay::changeRefreshRate(Fps fps) {
mCurrentFps = fps;
const auto buffer = getOrCreateBuffers(fps)[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
@@ -312,7 +329,7 @@
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a465a36..a2966e6 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -33,6 +33,20 @@
class GraphicBuffer;
class SurfaceControl;
+class SurfaceFlinger;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+ explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+ ~SurfaceControlHolder();
+
+ const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+ sp<SurfaceControl> mSurfaceControl;
+};
class RefreshRateOverlay {
public:
@@ -75,7 +89,7 @@
const FpsRange mFpsRange; // For color interpolation.
const bool mShowSpinner;
- const sp<SurfaceControl> mSurfaceControl;
+ const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
} // namespace android
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..a202dda 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -129,7 +129,6 @@
#include "LayerProtoHelper.h"
#include "LayerRenderArea.h"
#include "LayerVector.h"
-#include "MonitoredProducer.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "RefreshRateOverlay.h"
@@ -341,7 +340,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 +2255,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 +3114,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 +3159,7 @@
if (mSomeChildrenChanged) {
mVisibleRegionsDirty = true;
mSomeChildrenChanged = false;
+ mUpdateInputInfo = true;
}
// Update transform hint.
@@ -3221,6 +3223,7 @@
mLayersAdded = false;
// Layers have been added.
mVisibleRegionsDirty = true;
+ mUpdateInputInfo = true;
}
// some layers might have been removed, so
@@ -3228,6 +3231,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 +3256,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 +3272,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 +4105,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 +4565,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 +4623,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 +4930,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 +6816,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..bd4330f 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
@@ -378,7 +377,6 @@
friend class FpsReporter;
friend class TunnelModeEnabledReporter;
friend class Layer;
- friend class MonitoredProducer;
friend class RefreshRateOverlay;
friend class RegionSamplingThread;
friend class LayerRenderArea;
@@ -1198,7 +1196,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/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 39a5d0f..2399a39 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -22,14 +22,12 @@
#include <cutils/properties.h>
#include <ui/GraphicBuffer.h>
-#include "BufferLayerConsumer.h"
#include "BufferStateLayer.h"
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
-#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerDefaultFactory.h"
@@ -83,18 +81,6 @@
BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
}
-sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer(
- const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer) {
- return new MonitoredProducer(producer, flinger, layer);
-}
-
-sp<BufferLayerConsumer> DefaultFactory::createBufferLayerConsumer(
- const sp<IGraphicBufferConsumer>& consumer, renderengine::RenderEngine& renderEngine,
- uint32_t textureName, Layer* layer) {
- return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-}
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface(
const sp<IGraphicBufferProducer>& producer) {
return surfaceflinger::impl::createNativeWindowSurface(producer);
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 173ca81..4000e09 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -38,12 +38,6 @@
void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) override;
- sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
- const sp<SurfaceFlinger>&,
- const wp<Layer>&) override;
- sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
- renderengine::RenderEngine&, uint32_t tex,
- Layer*) override;
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>&) override;
std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index e117e96..77a75c4 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -85,12 +85,6 @@
virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) = 0;
- virtual sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
- const sp<SurfaceFlinger>&,
- const wp<Layer>&) = 0;
- virtual sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
- renderengine::RenderEngine&,
- uint32_t tex, Layer*) = 0;
virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>&) = 0;
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 5e20b74..c4b1b0f 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -71,19 +71,6 @@
sp<IGraphicBufferConsumer>* /* outConsumer */,
bool /* consumerIsSurfaceFlinger */) override {}
- sp<IGraphicBufferProducer> createMonitoredProducer(
- const sp<IGraphicBufferProducer>& /* producer */,
- const sp<SurfaceFlinger>& /* flinger */, const wp<Layer>& /* layer */) override {
- return nullptr;
- }
-
- sp<BufferLayerConsumer> createBufferLayerConsumer(
- const sp<IGraphicBufferConsumer>& /* consumer */,
- renderengine::RenderEngine& /* renderEngine */, uint32_t /* textureName */,
- Layer* /* layer */) override {
- return nullptr;
- }
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>& /* producer */) override {
return nullptr;
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/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 456a498..60c46a9 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -333,18 +333,6 @@
mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
}
- sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer> &producer,
- const sp<SurfaceFlinger> &flinger,
- const wp<Layer> &layer) override {
- return new MonitoredProducer(producer, flinger, layer);
- }
-
- sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer> &consumer,
- renderengine::RenderEngine &renderEngine,
- uint32_t textureName, Layer *layer) override {
- return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
- }
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer> &producer) override {
if (!mCreateNativeWindowSurface) return nullptr;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 4aef017..62a7997 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -18,9 +18,7 @@
#include <Client.h>
#include <DisplayDevice.h>
#include <EffectLayer.h>
-#include <LayerRejecter.h>
#include <LayerRenderArea.h>
-#include <MonitoredProducer.h>
#include <ftl/future.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <gui/IProducerListener.h>
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 19eaa19..f6ebfe5 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -629,7 +629,8 @@
EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
EXPECT_EQ(false, layer.source.buffer.isOpaque);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -679,7 +680,8 @@
EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2]),
layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -757,7 +759,8 @@
const renderengine::LayerSettings layer = layerSettings.back();
EXPECT_THAT(layer.source.buffer.buffer, IsNull());
EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(1.0f, layer.alpha);
return resultFuture;
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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3a05e2f..e8380eb 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -111,18 +111,6 @@
mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
}
- sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>& producer,
- const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer) override {
- return new MonitoredProducer(producer, flinger, layer);
- }
-
- sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>& consumer,
- renderengine::RenderEngine& renderEngine,
- uint32_t textureName, Layer* layer) override {
- return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
- }
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>& producer) override {
if (!mCreateNativeWindowSurface) return nullptr;
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{