Merge "SF: Add a sysprop for game default frame rate" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cd8f3cd..9c01169 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -7,7 +7,8 @@
"include-filter": "*"
},
{
- "exclude-filter": "*ChildLayerTest#ChildrenSurviveParentDestruction"
+ // TODO(b/305717998): Deflake and re-enable
+ "exclude-filter": "*ChildLayerTest*"
}
]
},
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index d73a30b..e00c2a2 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -24,7 +24,6 @@
shared_libs: [
"libbase",
- "libbinder", // also contains servicemanager_interface
"libvintf",
"libcutils",
"liblog",
@@ -33,6 +32,21 @@
],
target: {
+ android: {
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
vendor: {
exclude_shared_libs: ["libvintf"],
},
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 1418e1f..e286a84 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -275,12 +275,6 @@
}
prebuilt_etc {
- name: "android.hardware.telephony.satellite.prebuilt.xml",
- src: "android.hardware.telephony.satellite.xml",
- defaults: ["frameworks_native_data_etc_defaults"],
-}
-
-prebuilt_etc {
name: "android.hardware.thread_network.prebuilt.xml",
src: "android.hardware.thread_network.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
deleted file mode 100644
index 5966cba..0000000
--- a/data/etc/android.hardware.telephony.satellite.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. -->
-<permissions>
- <feature name="android.hardware.telephony.satellite" />
-</permissions>
diff --git a/data/etc/android.hardware.type.automotive.xml b/data/etc/android.hardware.type.automotive.xml
index a9b4b05..8605d18 100644
--- a/data/etc/android.hardware.type.automotive.xml
+++ b/data/etc/android.hardware.type.automotive.xml
@@ -17,4 +17,6 @@
<!-- These features determine that the device running android is a car. -->
<permissions>
<feature name="android.hardware.type.automotive" />
+
+ <unavailable-feature name="android.software.picture_in_picture"/>
</permissions>
diff --git a/data/etc/aosp_excluded_hardware.xml b/data/etc/aosp_excluded_hardware.xml
index c12f435..013f278 100644
--- a/data/etc/aosp_excluded_hardware.xml
+++ b/data/etc/aosp_excluded_hardware.xml
@@ -18,5 +18,4 @@
<!-- This should be used to exclude this feature from aosp targets. As aosp configurations
may or may not have a valid location provider -->
<unavailable-feature name="android.hardware.location.network" />
- <unavailable-feature name="android.software.device_id_attestation" />
</permissions>
diff --git a/include/input/Input.h b/include/input/Input.h
index 567361d..bd544b5 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -257,6 +257,10 @@
bool isStylusToolType(ToolType toolType);
+struct PointerProperties;
+
+bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& properties);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 6c2b313..620c23c 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -118,7 +118,8 @@
],
srcs: [
- "OS.cpp",
+ "OS_android.cpp",
+ "OS_unix_base.cpp",
"RpcTransportRaw.cpp",
],
@@ -249,6 +250,7 @@
srcs: [
// Trusty-specific files
+ "OS_android.cpp",
"trusty/logging.cpp",
"trusty/OS.cpp",
"trusty/RpcServerTrusty.cpp",
@@ -357,6 +359,38 @@
}
cc_library_static {
+ name: "libbinder_rpc_no_blob",
+ vendor_available: true,
+ defaults: [
+ "libbinder_common_defaults",
+ "libbinder_android_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_DISABLE_BLOB",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
+ name: "libbinder_rpc_no_native_handle",
+ vendor_available: true,
+ defaults: [
+ "libbinder_common_defaults",
+ "libbinder_android_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_DISABLE_NATIVE_HANDLE",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
name: "libbinder_rpc_single_threaded",
defaults: [
"libbinder_common_defaults",
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index aa67869..f22e90a 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -29,7 +29,6 @@
#include <binder/Parcel.h>
#include <binder/RecordedTransaction.h>
#include <binder/RpcServer.h>
-#include <private/android_filesystem_config.h>
#include <pthread.h>
#include <utils/misc.h>
@@ -41,10 +40,13 @@
#endif
#include "BuildFlags.h"
+#include "OS.h"
#include "RpcState.h"
namespace android {
+constexpr uid_t kUidRoot = 0;
+
// Service implementations inherit from BBinder and IBinder, and this is frozen
// in prebuilts.
#ifdef __LP64__
@@ -300,7 +302,7 @@
return INVALID_OPERATION;
}
uid_t uid = IPCThreadState::self()->getCallingUid();
- if (uid != AID_ROOT) {
+ if (uid != kUidRoot) {
ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid);
return PERMISSION_DENIED;
}
@@ -330,7 +332,7 @@
return INVALID_OPERATION;
}
uid_t uid = IPCThreadState::self()->getCallingUid();
- if (uid != AID_ROOT) {
+ if (uid != kUidRoot) {
ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid);
return PERMISSION_DENIED;
}
@@ -634,7 +636,7 @@
return INVALID_OPERATION;
}
uid_t uid = IPCThreadState::self()->getCallingUid();
- if (uid != AID_ROOT) {
+ if (uid != kUidRoot) {
ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid);
return PERMISSION_DENIED;
}
@@ -794,7 +796,7 @@
}
case SYSPROPS_TRANSACTION: {
- report_sysprop_change();
+ if (!binder::os::report_sysprop_change()) return INVALID_OPERATION;
return NO_ERROR;
}
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index fecae31..8dc1f6a 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -23,7 +23,7 @@
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
-namespace android {
+namespace android::binder::os {
android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
@@ -41,4 +41,8 @@
const RpcTransportFd& socket, iovec* iovs, int niovs,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
-} // namespace android
+uint64_t GetThreadId();
+
+bool report_sysprop_change();
+
+} // namespace android::binder::os
diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp
new file mode 100644
index 0000000..ad458eb
--- /dev/null
+++ b/libs/binder/OS_android.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 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/threads.h>
+#include <utils/misc.h>
+
+namespace android::binder::os {
+
+uint64_t GetThreadId() {
+#ifdef BINDER_RPC_SINGLE_THREADED
+ return 0;
+#else
+ return base::GetThreadId();
+#endif
+}
+
+bool report_sysprop_change() {
+ android::report_sysprop_change();
+ return true;
+}
+
+} // namespace android::binder::os
diff --git a/libs/binder/OS.cpp b/libs/binder/OS_unix_base.cpp
similarity index 98%
rename from libs/binder/OS.cpp
rename to libs/binder/OS_unix_base.cpp
index ce60e33..81933d5 100644
--- a/libs/binder/OS.cpp
+++ b/libs/binder/OS_unix_base.cpp
@@ -24,7 +24,7 @@
using android::base::ErrnoError;
using android::base::Result;
-namespace android {
+namespace android::binder::os {
// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
constexpr size_t kMaxFdsPerMsg = 253;
@@ -162,4 +162,4 @@
return TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
}
-} // namespace android
+} // namespace android::binder::os
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 6e3abf5..a3ff7d2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -40,7 +40,9 @@
#include <binder/TextOutput.h>
#include <android-base/scopeguard.h>
+#ifndef BINDER_DISABLE_BLOB
#include <cutils/ashmem.h>
+#endif
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -662,7 +664,7 @@
// To match kernel binder behavior, we always dup, even if the
// FD was unowned in the source parcel.
int newFd = -1;
- if (status_t status = dupFileDescriptor(oldFd, &newFd); status != OK) {
+ if (status_t status = binder::os::dupFileDescriptor(oldFd, &newFd); status != OK) {
ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status));
}
rpcFields->mFds->emplace_back(base::unique_fd(newFd));
@@ -1475,6 +1477,7 @@
return writeParcelable(*parcelable);
}
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
status_t Parcel::writeNativeHandle(const native_handle* handle)
{
if (!handle || handle->version != sizeof(native_handle))
@@ -1497,6 +1500,7 @@
err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts);
return err;
}
+#endif
status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) {
if (auto* rpcFields = maybeRpcFields()) {
@@ -1554,7 +1558,7 @@
status_t Parcel::writeDupFileDescriptor(int fd)
{
int dupFd;
- if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) {
+ if (status_t err = binder::os::dupFileDescriptor(fd, &dupFd); err != OK) {
return err;
}
status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/);
@@ -1573,7 +1577,7 @@
status_t Parcel::writeDupParcelFileDescriptor(int fd)
{
int dupFd;
- if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) {
+ if (status_t err = binder::os::dupFileDescriptor(fd, &dupFd); err != OK) {
return err;
}
status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/);
@@ -1589,6 +1593,12 @@
status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob)
{
+#ifdef BINDER_DISABLE_BLOB
+ (void)len;
+ (void)mutableCopy;
+ (void)outBlob;
+ return INVALID_OPERATION;
+#else
if (len > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
@@ -1640,6 +1650,7 @@
}
::close(fd);
return status;
+#endif
}
status_t Parcel::writeDupImmutableBlobFileDescriptor(int fd)
@@ -2271,6 +2282,7 @@
return status.exceptionCode();
}
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
native_handle* Parcel::readNativeHandle() const
{
int numFds, numInts;
@@ -2303,6 +2315,7 @@
}
return h;
}
+#endif
int Parcel::readFileDescriptor() const {
if (const auto* rpcFields = maybeRpcFields()) {
@@ -2386,7 +2399,7 @@
}
int dupFd;
- if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) {
+ if (status_t err = binder::os::dupFileDescriptor(got, &dupFd); err != OK) {
return BAD_VALUE;
}
@@ -2408,7 +2421,7 @@
}
int dupFd;
- if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) {
+ if (status_t err = binder::os::dupFileDescriptor(got, &dupFd); err != OK) {
return BAD_VALUE;
}
@@ -2423,6 +2436,11 @@
status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const
{
+#ifdef BINDER_DISABLE_BLOB
+ (void)len;
+ (void)outBlob;
+ return INVALID_OPERATION;
+#else
int32_t blobType;
status_t status = readInt32(&blobType);
if (status) return status;
@@ -2456,6 +2474,7 @@
outBlob->init(fd, ptr, len, isMutable);
return NO_ERROR;
+#endif
}
status_t Parcel::read(FlattenableHelperInterface& val) const
@@ -3204,6 +3223,7 @@
}
size_t openAshmemSize = 0;
+#ifndef BINDER_DISABLE_BLOB
for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
const flat_binder_object* flat =
reinterpret_cast<const flat_binder_object*>(mData + kernelFields->mObjects[i]);
@@ -3218,6 +3238,7 @@
}
}
}
+#endif
return openAshmemSize;
}
#endif // BINDER_WITH_KERNEL_IPC
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 55fc16d..07ab093 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "RpcServer"
#include <inttypes.h>
+#include <netinet/tcp.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -24,7 +25,6 @@
#include <thread>
#include <vector>
-#include <android-base/hex.h>
#include <android-base/scopeguard.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
@@ -57,7 +57,7 @@
sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
// Default is without TLS.
if (rpcTransportCtxFactory == nullptr)
- rpcTransportCtxFactory = makeDefaultRpcTransportCtxFactory();
+ rpcTransportCtxFactory = binder::os::makeDefaultRpcTransportCtxFactory();
auto ctx = rpcTransportCtxFactory->newServerCtx();
if (ctx == nullptr) return nullptr;
return sp<RpcServer>::make(std::move(ctx));
@@ -216,7 +216,7 @@
iovec iov{&zero, sizeof(zero)};
std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
- ssize_t num_bytes = receiveMessageFromSocket(server.mServer, &iov, 1, &fds);
+ ssize_t num_bytes = binder::os::receiveMessageFromSocket(server.mServer, &iov, 1, &fds);
if (num_bytes < 0) {
int savedErrno = errno;
ALOGE("Failed recvmsg: %s", strerror(savedErrno));
@@ -231,7 +231,7 @@
}
unique_fd fd(std::move(std::get<unique_fd>(fds.back())));
- if (auto res = setNonBlocking(fd); !res.ok()) {
+ if (auto res = binder::os::setNonBlocking(fd); !res.ok()) {
ALOGE("Failed setNonBlocking: %s", res.error().message().c_str());
return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
}
@@ -484,11 +484,11 @@
// don't block if there is some entropy issue
if (tries++ > 5) {
ALOGE("Cannot find new address: %s",
- base::HexString(sessionId.data(), sessionId.size()).c_str());
+ HexString(sessionId.data(), sessionId.size()).c_str());
return;
}
- auto status = getRandomBytes(sessionId.data(), sessionId.size());
+ auto status = binder::os::getRandomBytes(sessionId.data(), sessionId.size());
if (status != OK) {
ALOGE("Failed to read random session ID: %s", strerror(-status));
return;
@@ -536,7 +536,7 @@
auto it = server->mSessions.find(sessionId);
if (it == server->mSessions.end()) {
ALOGE("Cannot add thread, no record of session with ID %s",
- base::HexString(sessionId.data(), sessionId.size()).c_str());
+ HexString(sessionId.data(), sessionId.size()).c_str());
return;
}
session = it->second;
@@ -572,6 +572,17 @@
return -savedErrno;
}
+ if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) {
+ int noDelay = 1;
+ int result =
+ setsockopt(socket_fd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay));
+ if (result < 0) {
+ int savedErrno = errno;
+ ALOGE("Could not set TCP_NODELAY on %s", strerror(savedErrno));
+ return -savedErrno;
+ }
+ }
+
{
RpcMutexLockGuard _l(mLock);
if (mServerSocketModifier != nullptr) {
@@ -610,15 +621,14 @@
void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) {
const std::vector<uint8_t>& id = session->mId;
LOG_ALWAYS_FATAL_IF(id.empty(), "Server sessions must be initialized with ID");
- LOG_RPC_DETAIL("Dropping session with address %s",
- base::HexString(id.data(), id.size()).c_str());
+ LOG_RPC_DETAIL("Dropping session with address %s", HexString(id.data(), id.size()).c_str());
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());
+ HexString(id.data(), id.size()).c_str());
LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %s",
- base::HexString(id.data(), id.size()).c_str());
+ HexString(id.data(), id.size()).c_str());
(void)mSessions.erase(it);
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index c3dee16..fa8f2b5 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -26,7 +26,6 @@
#include <string_view>
-#include <android-base/hex.h>
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
#include <binder/BpBinder.h>
@@ -70,7 +69,7 @@
sp<RpcSession> RpcSession::make() {
// Default is without TLS.
- return make(makeDefaultRpcTransportCtxFactory());
+ return make(binder::os::makeDefaultRpcTransportCtxFactory());
}
sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
@@ -195,7 +194,7 @@
fd = request();
if (!fd.ok()) return BAD_VALUE;
}
- if (auto res = setNonBlocking(fd); !res.ok()) {
+ if (auto res = binder::os::setNonBlocking(fd); !res.ok()) {
ALOGE("setupPreconnectedClient: %s", res.error().message().c_str());
return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
}
@@ -310,8 +309,7 @@
status = state()->getSessionId(connection.get(), sp<RpcSession>::fromExisting(this), &mId);
if (status != OK) return status;
- LOG_RPC_DETAIL("RpcSession %p has id %s", this,
- base::HexString(mId.data(), mId.size()).c_str());
+ LOG_RPC_DETAIL("RpcSession %p has id %s", this, HexString(mId.data(), mId.size()).c_str());
return OK;
}
@@ -709,7 +707,7 @@
std::nullopt, nullptr);
if (sendSessionIdStatus != OK) {
ALOGE("Could not write session ID ('%s') to socket: %s",
- base::HexString(sessionId.data(), sessionId.size()).c_str(),
+ HexString(sessionId.data(), sessionId.size()).c_str(),
statusToString(sendSessionIdStatus).c_str());
return sendSessionIdStatus;
}
@@ -770,7 +768,7 @@
{
RpcMutexLockGuard _l(mMutex);
connection->rpcTransport = std::move(rpcTransport);
- connection->exclusiveTid = rpcGetThreadId();
+ connection->exclusiveTid = binder::os::GetThreadId();
mConnections.mOutgoing.push_back(connection);
}
@@ -825,7 +823,7 @@
sp<RpcConnection> session = sp<RpcConnection>::make();
session->rpcTransport = std::move(rpcTransport);
- session->exclusiveTid = rpcGetThreadId();
+ session->exclusiveTid = binder::os::GetThreadId();
mConnections.mIncoming.push_back(session);
mConnections.mMaxIncoming = mConnections.mIncoming.size();
@@ -870,7 +868,7 @@
connection->mConnection = nullptr;
connection->mReentrant = false;
- uint64_t tid = rpcGetThreadId();
+ uint64_t tid = binder::os::GetThreadId();
RpcMutexUniqueLock _l(session->mMutex);
session->mConnections.mWaitingThreads++;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index bac2808..5046253 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,7 +18,6 @@
#include "RpcState.h"
-#include <android-base/hex.h>
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
@@ -363,7 +362,7 @@
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(),
- android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
+ HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
}
if (status_t status =
@@ -398,7 +397,7 @@
for (int i = 0; i < niovs; i++) {
LOG_RPC_DETAIL("Received %s (part %d of %d) on RpcTransport %p: %s",
what, i + 1, niovs, connection->rpcTransport.get(),
- android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
+ HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
}
return OK;
}
@@ -409,10 +408,11 @@
char codename[PROPERTY_VALUE_MAX];
property_get("ro.build.version.codename", codename, "");
if (!strcmp(codename, "REL")) {
- ALOGE("Cannot use experimental RPC binder protocol on a release branch.");
+ ALOGE("Cannot use experimental RPC binder protocol in a release configuration.");
return false;
}
#else
+ // TODO(b/305983144)
// don't restrict on other platforms, though experimental should
// only really be used for testing, we don't have a good way to see
// what is shipping outside of Android
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index ddbcb57..c089811 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -59,8 +59,8 @@
override {
bool sentFds = false;
auto send = [&](iovec* iovs, int niovs) -> ssize_t {
- ssize_t ret =
- sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds);
+ ssize_t ret = binder::os::sendMessageOnSocket(mSocket, iovs, niovs,
+ sentFds ? nullptr : ancillaryFds);
sentFds |= ret > 0;
return ret;
};
@@ -73,7 +73,7 @@
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 {
- return receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds);
+ return binder::os::receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds);
};
return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN,
altPoll);
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
index 0314b0f..47fd17d 100644
--- a/libs/binder/Utils.cpp
+++ b/libs/binder/Utils.cpp
@@ -16,6 +16,7 @@
#include "Utils.h"
+#include <android-base/logging.h>
#include <string.h>
namespace android {
@@ -24,4 +25,22 @@
memset(data, 0, size);
}
+std::string HexString(const void* bytes, size_t len) {
+ CHECK(bytes != nullptr || len == 0) << bytes << " " << len;
+
+ // b/132916539: Doing this the 'C way', std::setfill triggers ubsan implicit conversion
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ const char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ const auto c = bytes8[i];
+ result[2 * i] = chars[c >> 4];
+ result[2 * i + 1] = chars[c & 0xf];
+ }
+
+ return result;
+}
+
} // namespace android
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index e04199c..dd632c0 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -70,4 +70,10 @@
}
};
+// Converts binary data into a hexString.
+//
+// Hex values are printed in order, e.g. 0xDEAD will result in 'adde' because
+// Android is little-endian.
+std::string HexString(const void* bytes, size_t len);
+
} // namespace android
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 45e5ace..98d12bb 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -26,7 +26,9 @@
#include <vector>
#include <android-base/unique_fd.h>
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
#include <cutils/native_handle.h>
+#endif
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
@@ -324,11 +326,13 @@
template<typename T>
status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
// Place a native_handle into the parcel (the native_handle's file-
// descriptors are dup'ed, so it is safe to delete the native_handle
// when this function returns).
// Doesn't take ownership of the native_handle.
status_t writeNativeHandle(const native_handle* handle);
+#endif
// Place a file descriptor into the parcel. The given fd must remain
// valid for the lifetime of the parcel.
@@ -559,13 +563,14 @@
// response headers rather than doing it by hand.
int32_t readExceptionCode() const;
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
// Retrieve native_handle from the parcel. This returns a copy of the
// parcel's native_handle (the caller takes ownership). The caller
- // must free the native_handle with native_handle_close() and
+ // must free the native_handle with native_handle_close() and
// native_handle_delete().
native_handle* readNativeHandle() const;
+#endif
-
// Retrieve a file descriptor from the parcel. This returns the raw fd
// in the parcel, which you do not own -- use dup() to get your own copy.
int readFileDescriptor() const;
@@ -1289,6 +1294,7 @@
// Fields only needed when parcelling for "kernel Binder".
struct KernelFields {
+ KernelFields() {}
binder_size_t* mObjects = nullptr;
size_t mObjectsSize = 0;
size_t mObjectsCapacity = 0;
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
index b80d116..d25f292 100644
--- a/libs/binder/include/binder/RpcThreads.h
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -17,8 +17,6 @@
#include <pthread.h>
-#include <android-base/threads.h>
-
#include <condition_variable>
#include <functional>
#include <memory>
@@ -121,10 +119,6 @@
}
} // namespace rpc_this_thread
-static inline uint64_t rpcGetThreadId() {
- return 0;
-}
-
static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) {
t.join();
}
@@ -136,10 +130,6 @@
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
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index f7dd9c9..47da296 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -21,7 +21,9 @@
#include <android/binder_status.h>
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
+#if __has_include(<private/android_filesystem_config.h>)
#include <private/android_filesystem_config.h>
+#endif
#include "ibinder_internal.h"
#include "parcel_internal.h"
@@ -229,7 +231,11 @@
// Shell commands should only be callable by ADB.
uid_t uid = AIBinder_getCallingUid();
- if (uid != AID_ROOT && uid != AID_SHELL) {
+ if (uid != 0 /* root */
+#ifdef AID_SHELL
+ && uid != AID_SHELL
+#endif
+ ) {
if (resultReceiver != nullptr) {
resultReceiver->send(-1);
}
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 76acff5..3ee36cd 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -156,7 +156,10 @@
}
sp<IFoo> IFoo::getService(const char* instance, AIBinder** outBinder) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
AIBinder* binder = AServiceManager_getService(instance); // maybe nullptr
+#pragma clang diagnostic pop
if (binder == nullptr) {
return nullptr;
}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 15708ca..cab1a60 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -341,7 +341,10 @@
// libbinder across processes to the NDK service which doesn't implement
// shell
static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName));
+#pragma clang diagnostic pop
Vector<String16> argsVec;
EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO,
@@ -384,7 +387,10 @@
// checkService on it, since the other process serving it might not be started yet.
{
// getService, not waitForService, to take advantage of timeout
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
auto binder = ndk::SpAIBinder(AServiceManager_getService(IFoo::kSomeInstanceName));
+#pragma clang diagnostic pop
ASSERT_NE(nullptr, binder.get());
}
@@ -574,7 +580,10 @@
}
TEST(NdkBinder, RetrieveNonNdkService) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+#pragma clang diagnostic pop
ASSERT_NE(nullptr, binder);
EXPECT_TRUE(AIBinder_isRemote(binder));
EXPECT_TRUE(AIBinder_isAlive(binder));
@@ -588,7 +597,10 @@
}
TEST(NdkBinder, LinkToDeath) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+#pragma clang diagnostic pop
ASSERT_NE(nullptr, binder);
AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath);
@@ -618,7 +630,10 @@
}
TEST(NdkBinder, SetInheritRtNonLocal) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+#pragma clang diagnostic pop
ASSERT_NE(binder, nullptr);
ASSERT_TRUE(AIBinder_isRemote(binder));
@@ -654,11 +669,14 @@
}
TEST(NdkBinder, EqualityOfRemoteBinderPointer) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService);
ASSERT_NE(nullptr, binderA);
AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService);
ASSERT_NE(nullptr, binderB);
+#pragma clang diagnostic pop
EXPECT_EQ(binderA, binderB);
@@ -672,7 +690,10 @@
}
TEST(NdkBinder, ABpBinderRefCount) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+#pragma clang diagnostic pop
AIBinder_Weak* wBinder = AIBinder_Weak_new(binder);
ASSERT_NE(nullptr, binder);
@@ -695,7 +716,10 @@
}
TEST(NdkBinder, RequestedSidWorks) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+#pragma clang diagnostic pop
std::shared_ptr<aidl::IBinderNdkUnitTest> service =
aidl::IBinderNdkUnitTest::fromBinder(binder);
@@ -718,7 +742,10 @@
std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>();
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+#pragma clang diagnostic pop
std::shared_ptr<aidl::IBinderNdkUnitTest> service =
aidl::IBinderNdkUnitTest::fromBinder(binder);
@@ -741,7 +768,10 @@
TEST(NdkBinder, ConvertToPlatformBinder) {
for (const ndk::SpAIBinder& binder :
{// remote
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)),
+#pragma clang diagnostic pop
// local
ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
// convert to platform binder
@@ -774,7 +804,10 @@
TEST(NdkBinder, GetAndVerifyScopedAIBinder_Weak) {
for (const ndk::SpAIBinder& binder :
{// remote
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)),
+#pragma clang diagnostic pop
// local
ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
// get a const ScopedAIBinder_Weak and verify promote
@@ -869,7 +902,10 @@
TEST(NdkBinder, UseHandleShellCommand) {
static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestService));
+#pragma clang diagnostic pop
EXPECT_EQ("", shellCmdToString(testService, {}));
EXPECT_EQ("", shellCmdToString(testService, {"", ""}));
@@ -879,7 +915,10 @@
TEST(NdkBinder, FlaggedServiceAccessible) {
static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestServiceFlagged));
+#pragma clang diagnostic pop
ASSERT_NE(nullptr, testService);
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 463c210..a08cb7a 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -436,7 +436,7 @@
impl<I: FromIBinder + ?Sized> PartialOrd for Strong<I> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- self.0.as_binder().partial_cmp(&other.0.as_binder())
+ Some(self.cmp(other))
}
}
@@ -483,7 +483,7 @@
impl<I: FromIBinder + ?Sized> PartialOrd for Weak<I> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- self.weak_binder.partial_cmp(&other.weak_binder)
+ Some(self.cmp(other))
}
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 8841fe6..ed870b6 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -144,6 +144,7 @@
#[doc(hidden)]
pub mod unstable_api {
pub use crate::binder::AsNative;
+ pub use crate::error::status_result;
pub use crate::proxy::unstable_api::new_spibinder;
pub use crate::sys::AIBinder;
pub use crate::sys::AParcel;
diff --git a/libs/binder/rust/tests/serialization.hpp b/libs/binder/rust/tests/serialization.hpp
index 0041608..9edcd6d 100644
--- a/libs/binder/rust/tests/serialization.hpp
+++ b/libs/binder/rust/tests/serialization.hpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpragma-once-outside-header"
#pragma once
+#pragma clang diagnostic pop
#include <binder/IBinder.h>
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 8e95d69..5bf9680 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -279,7 +279,10 @@
while (-1 != (opt = getopt(argc, argv, "gi:"))) {
switch (opt) {
case 'g': {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
serviceRetriever = &android::IServiceManager::getService;
+#pragma clang diagnostic pop
} break;
case 'i': {
ip_address = optarg;
diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp
index 307151c..e43ee5f 100644
--- a/libs/binder/tests/binderClearBufTest.cpp
+++ b/libs/binder/tests/binderClearBufTest.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <android-base/hex.h>
#include <android-base/logging.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
@@ -24,6 +23,8 @@
#include <binder/Stability.h>
#include <gtest/gtest.h>
+#include "../Utils.h"
+
#include <sys/prctl.h>
#include <thread>
@@ -68,13 +69,16 @@
lastReply = reply.data();
lastReplySize = reply.dataSize();
}
- *outBuffer = android::base::HexString(lastReply, lastReplySize);
+ *outBuffer = android::HexString(lastReply, lastReplySize);
return result;
}
};
TEST(BinderClearBuf, ClearKernelBuffer) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> binder = defaultServiceManager()->getService(kServerName);
+#pragma clang diagnostic pop
ASSERT_NE(nullptr, binder);
std::string replyBuffer;
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 77a5fa8..0075688 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -135,7 +135,10 @@
TEST_F(HostDeviceTest, GetService) {
auto sm = defaultServiceManager();
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
auto rpcBinder = sm->getService(String16(kServiceName));
+#pragma clang diagnostic pop
ASSERT_NE(nullptr, rpcBinder);
EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 0396869..341e9ce 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -213,7 +213,10 @@
sp<IServiceManager> sm = defaultServiceManager();
//printf("%s: pid %d, get service\n", __func__, m_pid);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
m_server = sm->getService(binderLibTestServiceName);
+#pragma clang diagnostic pop
ASSERT_TRUE(m_server != nullptr);
//printf("%s: pid %d, get service done\n", __func__, m_pid);
}
@@ -1107,6 +1110,7 @@
status_t ret;
data.writeInterfaceToken(binderLibTestServiceName);
ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
Parcel data2, reply2;
status_t ret2;
@@ -1595,9 +1599,8 @@
}
switch (code) {
case BINDER_LIB_TEST_REGISTER_SERVER: {
- int32_t id;
sp<IBinder> binder;
- id = data.readInt32();
+ /*id =*/data.readInt32();
binder = data.readStrongBinder();
if (binder == nullptr) {
return BAD_VALUE;
@@ -1993,7 +1996,10 @@
if (index == 0) {
ret = sm->addService(binderLibTestServiceName, testService);
} else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> server = sm->getService(binderLibTestServiceName);
+#pragma clang diagnostic pop
Parcel data, reply;
data.writeInt32(index);
data.writeStrongBinder(testService);
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
index 6773c95..d08a9bb 100644
--- a/libs/binder/tests/binderRecordReplayTest.cpp
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -133,7 +133,10 @@
public:
void SetUp() override {
// get the remote service
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
auto binder = defaultServiceManager()->getService(kServerName);
+#pragma clang diagnostic pop
ASSERT_NE(nullptr, binder);
mInterface = interface_cast<IBinderRecordReplayTest>(binder);
mBpBinder = binder->remoteBinder();
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 4c3c68e..1340ea1 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#ifndef __ANDROID_VENDOR__
+// only used on NDK tests outside of vendor
#include <aidl/IBinderRpcTest.h>
+#endif
#include <android-base/stringprintf.h>
#include <chrono>
@@ -227,7 +230,7 @@
std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
fds.emplace_back(std::move(sockServer));
- if (sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
+ if (binder::os::sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
int savedErrno = errno;
LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno);
}
@@ -1589,7 +1592,7 @@
int buf;
iovec iov{&buf, sizeof(buf)};
- if (receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
+ if (binder::os::receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
int savedErrno = errno;
LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
}
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index c1364dd..eceff35 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -70,12 +70,23 @@
return {RpcSecurity::RAW, RpcSecurity::TLS};
}
+static inline bool hasExperimentalRpc() {
+#ifdef __ANDROID__
+ return base::GetProperty("ro.build.version.codename", "") != "REL";
+#else
+ // TODO(b/305983144): restrict on other platforms
+ return true;
+#endif
+}
+
static inline std::vector<uint32_t> testVersions() {
std::vector<uint32_t> versions;
for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) {
versions.push_back(i);
}
- versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+ if (hasExperimentalRpc()) {
+ versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+ }
return versions;
}
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index e43508e..885bb45 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -50,7 +50,8 @@
TEST(BinderRpc, CanUseExperimentalWireVersion) {
auto session = RpcSession::make();
- EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+ EXPECT_EQ(hasExperimentalRpc(),
+ session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
}
TEST_P(BinderRpc, Ping) {
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 642cea4..7ec7c99 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <android-base/hex.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
@@ -25,6 +24,7 @@
#include <gtest/gtest.h>
#include "../Debug.h"
+#include "../Utils.h"
namespace android {
@@ -176,7 +176,7 @@
setParcelForRpc(&p, version);
kFillFuns[i](&p);
- result += base::HexString(p.data(), p.dataSize());
+ result += HexString(p.data(), p.dataSize());
}
return result;
}
@@ -263,16 +263,4 @@
}
}
-TEST(RpcWire, IfNotExperimentalCodeHasNoExperimentalFeatures) {
- if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
- GTEST_SKIP() << "Version is experimental, so experimental features are okay.";
- }
-
- // if we set the wire protocol version to experimental, none of the code
- // should introduce a difference (if this fails, it means we have features
- // which are enabled under experimental mode, but we aren't actually using
- // or testing them!)
- checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
-}
-
} // namespace android
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 5e8a32a..1c13866 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -605,7 +605,10 @@
static constexpr const char* getLogTag() { return "SafeInterfaceTest"; }
sp<ISafeInterfaceTest> getRemoteService() {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> binder = defaultServiceManager()->getService(kServiceName);
+#pragma clang diagnostic pop
sp<ISafeInterfaceTest> iface = interface_cast<ISafeInterfaceTest>(binder);
EXPECT_TRUE(iface != nullptr);
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 2398e1e..3d99358 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -155,7 +155,10 @@
}
TEST(BinderStability, ForceDowngradeToVendorStability) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
+#pragma clang diagnostic pop
auto server = interface_cast<IBinderStabilityTest>(serverBinder);
ASSERT_NE(nullptr, server.get());
@@ -206,7 +209,10 @@
EXPECT_EQ(connectionInfo, std::nullopt);
}
TEST(BinderStability, CantCallVendorBinderInSystemContext) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
+#pragma clang diagnostic pop
auto server = interface_cast<IBinderStabilityTest>(serverBinder);
ASSERT_NE(nullptr, server.get());
@@ -310,8 +316,11 @@
extern "C" void AIBinder_markVendorStability(AIBinder* binder); // <- BAD DO NOT COPY
TEST(BinderStability, NdkCantCallVendorBinderInSystemContext) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
SpAIBinder binder = SpAIBinder(AServiceManager_getService(
String8(kSystemStabilityServer).c_str()));
+#pragma clang diagnostic pop
std::shared_ptr<aidl::IBinderStabilityTest> remoteServer =
aidl::IBinderStabilityTest::fromBinder(binder);
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index cfaf2a9..0ea4a3f 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -204,7 +204,10 @@
for (int i = 0; i < server_count; i++) {
if (num == i)
continue;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
workers.push_back(serviceMgr->getService(generateServiceName(i)));
+#pragma clang diagnostic pop
}
// Run the benchmark if client
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 46d387c..416ffad 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -21,14 +21,15 @@
#include "parcelables/SingleDataParcelable.h"
#include "util.h"
-#include <android-base/hex.h>
#include <android/os/IServiceManager.h>
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
#include <binder/Status.h>
+#include "../../Utils.h"
+
using ::android::status_t;
-using ::android::base::HexString;
+using ::android::HexString;
enum ByteEnum : int8_t {};
enum IntEnum : int32_t {};
diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
index 438e8ae..cdc8bcc 100644
--- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
@@ -18,12 +18,13 @@
#include "hwbinder.h"
#include "util.h"
-#include <android-base/hex.h>
#include <android-base/logging.h>
#include <hwbinder/Parcel.h>
+#include "../../Utils.h"
+
using ::android::status_t;
-using ::android::base::HexString;
+using ::android::HexString;
// TODO: support scatter-gather types
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
index 5755239..071250d 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
@@ -15,7 +15,6 @@
*/
#include <android-base/file.h>
-#include <android-base/hex.h>
#include <android-base/logging.h>
#include <binder/Binder.h>
@@ -27,7 +26,6 @@
#include <vector>
using android::Parcel;
-using android::base::HexString;
using std::vector;
namespace android {
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index bef4ab6..5b1e9ea 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -22,7 +22,6 @@
#include <iostream>
-#include <android-base/hex.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
@@ -34,10 +33,12 @@
#include <sys/resource.h>
#include <sys/time.h>
+#include "../../Utils.h"
+
using android::fillRandomParcel;
using android::RandomParcelOptions;
using android::sp;
-using android::base::HexString;
+using android::HexString;
void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider,
RandomParcelOptions* options) {
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
index 7390d49..4a9bd07 100644
--- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
@@ -51,7 +51,9 @@
int flags = O_CLOEXEC;
if (provider->ConsumeBool()) flags |= O_DIRECT;
- if (provider->ConsumeBool()) flags |= O_NONBLOCK;
+
+ // TODO(b/236812909): also test blocking
+ if (true) flags |= O_NONBLOCK;
CHECK_EQ(0, pipe2(pipefds, flags)) << flags;
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
index 0035e4e..d3cd528 100644
--- a/libs/binder/tests/schd-dbg.cpp
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -340,7 +340,10 @@
for (int i = 0; i < server_count; i++) {
// self service is in-process so just skip
if (num == i) continue;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
workers.push_back(serviceMgr->getService(generateServiceName(i)));
+#pragma clang diagnostic pop
}
// Client for each pair iterates here
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 8ec9823..43e06e0 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -28,7 +28,7 @@
using android::base::Result;
-namespace android {
+namespace android::binder::os {
Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) {
// Trusty IPC syscalls are all non-blocking by default.
@@ -73,4 +73,4 @@
return -1;
}
-} // namespace android
+} // namespace android::binder::os
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index ab7a50d..1f05ef7 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -31,28 +31,22 @@
$(LIBBINDER_DIR)/FdTrigger.cpp \
$(LIBBINDER_DIR)/IInterface.cpp \
$(LIBBINDER_DIR)/IResultReceiver.cpp \
+ $(LIBBINDER_DIR)/OS_android.cpp \
$(LIBBINDER_DIR)/Parcel.cpp \
$(LIBBINDER_DIR)/Stability.cpp \
$(LIBBINDER_DIR)/Status.cpp \
$(LIBBINDER_DIR)/Utils.cpp \
$(LIBBASE_DIR)/hex.cpp \
$(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/Errors.cpp \
+ $(LIBUTILS_DIR)/binder/Errors.cpp \
+ $(LIBUTILS_DIR)/binder/RefBase.cpp \
+ $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
+ $(LIBUTILS_DIR)/binder/String16.cpp \
+ $(LIBUTILS_DIR)/binder/String8.cpp \
+ $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
+ $(LIBUTILS_DIR)/binder/Unicode.cpp \
+ $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
$(LIBUTILS_DIR)/misc.cpp \
- $(LIBUTILS_DIR)/RefBase.cpp \
- $(LIBUTILS_DIR)/StrongPointer.cpp \
- $(LIBUTILS_DIR)/Unicode.cpp \
-
-# TODO: remove the following when libbinder supports std::string
-# instead of String16 and String8 for Status and descriptors
-MODULE_SRCS += \
- $(LIBUTILS_DIR)/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/String16.cpp \
- $(LIBUTILS_DIR)/String8.cpp \
-
-# TODO: disable dump() transactions to get rid of Vector
-MODULE_SRCS += \
- $(LIBUTILS_DIR)/VectorImpl.cpp \
MODULE_DEFINES += \
LK_DEBUGLEVEL_NO_ALIASES=1 \
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 42db29a..2e56cbd 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -35,6 +35,7 @@
$(LIBBINDER_DIR)/FdTrigger.cpp \
$(LIBBINDER_DIR)/IInterface.cpp \
$(LIBBINDER_DIR)/IResultReceiver.cpp \
+ $(LIBBINDER_DIR)/OS_android.cpp \
$(LIBBINDER_DIR)/Parcel.cpp \
$(LIBBINDER_DIR)/ParcelFileDescriptor.cpp \
$(LIBBINDER_DIR)/RpcServer.cpp \
@@ -45,22 +46,15 @@
$(LIBBINDER_DIR)/Utils.cpp \
$(LIBBASE_DIR)/hex.cpp \
$(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/Errors.cpp \
+ $(LIBUTILS_DIR)/binder/Errors.cpp \
+ $(LIBUTILS_DIR)/binder/RefBase.cpp \
+ $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
+ $(LIBUTILS_DIR)/binder/String16.cpp \
+ $(LIBUTILS_DIR)/binder/String8.cpp \
+ $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
+ $(LIBUTILS_DIR)/binder/Unicode.cpp \
+ $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
$(LIBUTILS_DIR)/misc.cpp \
- $(LIBUTILS_DIR)/RefBase.cpp \
- $(LIBUTILS_DIR)/StrongPointer.cpp \
- $(LIBUTILS_DIR)/Unicode.cpp \
-
-# TODO: remove the following when libbinder supports std::string
-# instead of String16 and String8 for Status and descriptors
-MODULE_SRCS += \
- $(LIBUTILS_DIR)/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/String16.cpp \
- $(LIBUTILS_DIR)/String8.cpp \
-
-# TODO: disable dump() transactions to get rid of Vector
-MODULE_SRCS += \
- $(LIBUTILS_DIR)/VectorImpl.cpp \
MODULE_EXPORT_INCLUDES += \
$(LOCAL_DIR)/include \
diff --git a/libs/bufferstreams/rust/src/buffers/buffer.rs b/libs/bufferstreams/rust/src/buffers/buffer.rs
new file mode 100644
index 0000000..0a8516e
--- /dev/null
+++ b/libs/bufferstreams/rust/src/buffers/buffer.rs
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 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.
+
+//! Wrapper around the HardwareBuffer
+
+use nativewindow::*;
+
+use super::{buffer_owner::NoBufferOwner, BufferOwner};
+
+/// A wrapper for a hardware buffer.
+///
+/// This buffer may be associated with a buffer pool to which it will be returned to it when dropped.
+pub struct Buffer {
+ buffer_owner: Box<dyn BufferOwner>,
+ hardware_buffer: HardwareBuffer,
+}
+
+impl Buffer {
+ /// Create new buffer with a custom [BufferOwner].
+ pub fn new(buffer_owner: Box<dyn BufferOwner>, hardware_buffer: HardwareBuffer) -> Self {
+ Self { buffer_owner, hardware_buffer }
+ }
+
+ /// Create a new buffer with no association to any buffer pool.
+ pub fn new_unowned(hardware_buffer: HardwareBuffer) -> Self {
+ Self { buffer_owner: Box::new(NoBufferOwner), hardware_buffer }
+ }
+
+ /// Get the id of the underlying buffer.
+ pub fn id(&self) -> u64 {
+ self.hardware_buffer.id()
+ }
+
+ /// Get a reference to the underlying hardware buffer.
+ pub fn buffer(&self) -> &HardwareBuffer {
+ &self.hardware_buffer
+ }
+}
+
+impl Drop for Buffer {
+ fn drop(&mut self) {
+ self.buffer_owner.on_return(self);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ use crate::StreamConfig;
+
+ const STREAM_CONFIG: StreamConfig = StreamConfig {
+ width: 1,
+ height: 1,
+ layers: 1,
+ format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ stride: 0,
+ };
+
+ #[test]
+ fn test_get_buffer_id() {
+ let hardware_buffer = STREAM_CONFIG.create_hardware_buffer().unwrap();
+ let buffer_id = hardware_buffer.id();
+
+ let buffer = Buffer::new_unowned(hardware_buffer);
+ assert_eq!(buffer_id, buffer.id());
+ }
+}
diff --git a/libs/bufferstreams/rust/src/buffers/buffer_owner.rs b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs
new file mode 100644
index 0000000..a4abb9d
--- /dev/null
+++ b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::Buffer;
+
+/// Trait that represents an owner of a buffer that might need to handle events such as a buffer
+/// being dropped.
+pub trait BufferOwner {
+ /// Called when a buffer is dropped.
+ fn on_return(&self, buffer: &Buffer);
+}
+
+pub(super) struct NoBufferOwner;
+
+impl BufferOwner for NoBufferOwner {
+ fn on_return(&self, _buffer: &Buffer) {}
+}
diff --git a/libs/bufferstreams/rust/src/buffers/buffer_pool.rs b/libs/bufferstreams/rust/src/buffers/buffer_pool.rs
new file mode 100644
index 0000000..05804e2
--- /dev/null
+++ b/libs/bufferstreams/rust/src/buffers/buffer_pool.rs
@@ -0,0 +1,137 @@
+// Copyright (C) 2023 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.
+
+//! A Buffer Pool containing and managing HardwareBuffers
+
+use std::{
+ collections::HashMap,
+ sync::{Arc, Mutex, Weak},
+};
+
+use nativewindow::*;
+
+use crate::StreamConfig;
+
+use super::{Buffer, BufferOwner};
+
+pub(super) struct BufferPoolInner {
+ size: usize,
+ hardware_buffers: HashMap<u64, HardwareBuffer>,
+ available_buffers: Vec<u64>,
+}
+
+impl BufferPoolInner {
+ pub(super) fn return_buffer(&mut self, buffer_id: u64) {
+ assert!(self.hardware_buffers.contains_key(&buffer_id));
+ assert!(!self.available_buffers.contains(&buffer_id));
+
+ self.available_buffers.push(buffer_id);
+ }
+}
+
+struct BufferPoolOwner(Weak<Mutex<BufferPoolInner>>);
+
+impl BufferOwner for BufferPoolOwner {
+ fn on_return(&self, buffer: &Buffer) {
+ if let Some(locked_buffer_pool) = self.0.upgrade() {
+ let mut buffer_pool = locked_buffer_pool.lock().unwrap();
+
+ buffer_pool.return_buffer(buffer.id());
+ }
+ }
+}
+
+/// A thread-safe collection of buffers.
+///
+/// A buffer pool can be of arbitrary size. It creates and then holds references to all buffers
+/// associated with it.
+pub struct BufferPool(Arc<Mutex<BufferPoolInner>>);
+
+impl BufferPool {
+ /// Creates a new buffer pool of size pool_size. All buffers will be created according to
+ /// the stream config.
+ ///
+ /// This constructor creates all buffers at initialization.
+ pub fn new(pool_size: usize, stream_config: StreamConfig) -> Option<Self> {
+ let mut hardware_buffers = HashMap::new();
+ let mut available_buffers = Vec::new();
+ for _ in 0..pool_size {
+ if let Some(buffer) = stream_config.create_hardware_buffer() {
+ available_buffers.push(buffer.id());
+ hardware_buffers.insert(buffer.id(), buffer);
+ } else {
+ return None;
+ }
+ }
+ Some(Self(Arc::new(Mutex::new(BufferPoolInner {
+ size: pool_size,
+ hardware_buffers,
+ available_buffers,
+ }))))
+ }
+
+ /// Try to acquire the next available buffer in the buffer pool.
+ ///
+ /// If all buffers are in use it will return None.
+ pub fn next_buffer(&mut self) -> Option<Buffer> {
+ let mut inner = self.0.lock().unwrap();
+ if let Some(buffer_id) = inner.available_buffers.pop() {
+ Some(Buffer::new(
+ Box::new(BufferPoolOwner(Arc::downgrade(&self.0))),
+ inner.hardware_buffers[&buffer_id].clone(),
+ ))
+ } else {
+ None
+ }
+ }
+
+ /// Gets the size of the buffer pool.
+ pub fn size(&self) -> usize {
+ let inner = self.0.lock().unwrap();
+ inner.size
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ const STREAM_CONFIG: StreamConfig = StreamConfig {
+ width: 1,
+ height: 1,
+ layers: 1,
+ format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ stride: 0,
+ };
+
+ #[test]
+ fn buffer_pool_next_buffer() {
+ let mut buffer_pool = BufferPool::new(1, STREAM_CONFIG).unwrap();
+ let next_buffer = buffer_pool.next_buffer();
+
+ assert!(next_buffer.is_some());
+ assert!(buffer_pool.next_buffer().is_none());
+ }
+
+ #[test]
+ fn drop_buffer_returns_to_pool() {
+ let mut buffer_pool = BufferPool::new(1, STREAM_CONFIG).unwrap();
+ let next_buffer = buffer_pool.next_buffer();
+
+ assert!(next_buffer.is_some());
+ drop(next_buffer);
+ assert!(buffer_pool.next_buffer().is_some());
+ }
+}
diff --git a/libs/bufferstreams/rust/src/buffers/mod.rs b/libs/bufferstreams/rust/src/buffers/mod.rs
new file mode 100644
index 0000000..83360d6
--- /dev/null
+++ b/libs/bufferstreams/rust/src/buffers/mod.rs
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 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.
+
+//! Module containing Buffers and BufferPools
+
+mod buffer;
+mod buffer_owner;
+mod buffer_pool;
+
+pub use buffer::*;
+pub use buffer_owner::*;
+pub use buffer_pool::*;
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
index 5964281..be1525d 100644
--- a/libs/bufferstreams/rust/src/lib.rs
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -14,14 +14,15 @@
//! libbufferstreams: Reactive Streams for Graphics Buffers
+pub mod buffers;
pub mod publishers;
mod stream_config;
pub mod subscribers;
pub mod subscriptions;
+use buffers::Buffer;
pub use stream_config::*;
-use nativewindow::*;
use std::time::Instant;
/// This function will print Hello World.
@@ -158,8 +159,8 @@
/// Struct used to contain the buffer.
pub struct Frame {
- /// A handle to the C buffer interface.
- pub buffer: HardwareBuffer,
+ /// A buffer to be used this frame.
+ pub buffer: Buffer,
/// The time at which the buffer was dispatched.
pub present_time: Instant,
/// A fence used for reading/writing safely.
@@ -172,6 +173,8 @@
use super::*;
use anyhow::anyhow;
+ use buffers::Buffer;
+ use nativewindow::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
use std::borrow::BorrowMut;
use std::error::Error;
use std::ops::Add;
@@ -192,9 +195,11 @@
fn make_frame() -> Frame {
Frame {
- buffer: STREAM_CONFIG
- .create_hardware_buffer()
- .expect("Unable to create hardware buffer for test"),
+ buffer: Buffer::new_unowned(
+ STREAM_CONFIG
+ .create_hardware_buffer()
+ .expect("Unable to create hardware buffer for test"),
+ ),
present_time: Instant::now() + Duration::from_secs(1),
fence: 0,
}
diff --git a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
new file mode 100644
index 0000000..846105d
--- /dev/null
+++ b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
@@ -0,0 +1,112 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//!
+
+use std::time::Instant;
+
+use crate::{
+ buffers::BufferPool, subscriptions::SharedBufferSubscription, BufferPublisher,
+ BufferSubscriber, Frame, StreamConfig,
+};
+
+/// The [BufferPoolPublisher] submits buffers from a pool over to the subscriber.
+pub struct BufferPoolPublisher {
+ stream_config: StreamConfig,
+ buffer_pool: BufferPool,
+ subscription: SharedBufferSubscription,
+ subscriber: Option<Box<dyn BufferSubscriber>>,
+}
+
+impl BufferPoolPublisher {
+ /// The [BufferPoolPublisher] needs to initialize a [BufferPool], the [BufferPool] will create
+ /// all buffers at initialization using the stream_config.
+ pub fn new(stream_config: StreamConfig, size: usize) -> Option<Self> {
+ BufferPool::new(size, stream_config).map(|buffer_pool| Self {
+ stream_config,
+ buffer_pool,
+ subscription: SharedBufferSubscription::new(),
+ subscriber: None,
+ })
+ }
+
+ /// If the [SharedBufferSubscription] is ready for a [Frame], a buffer will be requested from
+ /// [BufferPool] and sent over to the [BufferSubscriber].
+ pub fn send_next_frame(&mut self, present_time: Instant) -> bool {
+ if let Some(subscriber) = self.subscriber.as_mut() {
+ if self.subscription.take_request() {
+ if let Some(buffer) = self.buffer_pool.next_buffer() {
+ let frame = Frame { buffer, present_time, fence: 0 };
+
+ subscriber.on_next(frame);
+ return true;
+ }
+ }
+ }
+ false
+ }
+}
+
+impl BufferPublisher for BufferPoolPublisher {
+ fn get_publisher_stream_config(&self) -> StreamConfig {
+ self.stream_config
+ }
+
+ fn subscribe(&mut self, subscriber: impl BufferSubscriber + 'static) {
+ assert!(self.subscriber.is_none());
+
+ self.subscriber = Some(Box::new(subscriber));
+ self.subscriber.as_mut().unwrap().on_subscribe(self.subscription.clone_for_subscriber());
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use nativewindow::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+
+ use super::*;
+
+ use crate::{
+ subscribers::{
+ testing::{TestSubscriber, TestingSubscriberEvent},
+ SharedSubscriber,
+ },
+ StreamConfig,
+ };
+
+ const STREAM_CONFIG: StreamConfig = StreamConfig {
+ width: 1,
+ height: 1,
+ layers: 1,
+ format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ stride: 0,
+ };
+
+ #[test]
+ fn test_send_next_frame() {
+ let subscriber = SharedSubscriber::new(TestSubscriber::new(STREAM_CONFIG));
+
+ let mut buffer_pool_publisher = BufferPoolPublisher::new(STREAM_CONFIG, 1).unwrap();
+ buffer_pool_publisher.subscribe(subscriber.clone());
+
+ subscriber.map_inner(|s| s.request(1));
+
+ assert!(buffer_pool_publisher.send_next_frame(Instant::now()));
+
+ let events = subscriber.map_inner_mut(|s| s.take_events());
+ assert!(matches!(events.last().unwrap(), TestingSubscriberEvent::Next(_)));
+ assert_eq!(buffer_pool_publisher.subscription.pending_requests(), 0);
+ }
+}
diff --git a/libs/bufferstreams/rust/src/publishers/mod.rs b/libs/bufferstreams/rust/src/publishers/mod.rs
index 2fd518e..8ed3ba0 100644
--- a/libs/bufferstreams/rust/src/publishers/mod.rs
+++ b/libs/bufferstreams/rust/src/publishers/mod.rs
@@ -14,4 +14,7 @@
//! This module provides [BufferSubscriber] implementations and helpers.
+mod buffer_pool_publisher;
pub mod testing;
+
+pub use buffer_pool_publisher::*;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index ffe79a3..29d64af 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -111,12 +111,14 @@
status_t JankData::writeToParcel(Parcel* output) const {
SAFE_PARCEL(output->writeInt64, frameVsyncId);
SAFE_PARCEL(output->writeInt32, jankType);
+ SAFE_PARCEL(output->writeInt64, frameIntervalNs);
return NO_ERROR;
}
status_t JankData::readFromParcel(const Parcel* input) {
SAFE_PARCEL(input->readInt64, &frameVsyncId);
SAFE_PARCEL(input->readInt32, &jankType);
+ SAFE_PARCEL(input->readInt64, &frameIntervalNs);
return NO_ERROR;
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 613721e..fd8fc8d 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -85,6 +85,7 @@
changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
frameRateCategory(ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT),
+ frameRateCategorySmoothSwitchOnly(false),
frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF),
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
@@ -162,6 +163,7 @@
SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeByte, defaultFrameRateCompatibility);
SAFE_PARCEL(output.writeByte, frameRateCategory);
+ SAFE_PARCEL(output.writeBool, frameRateCategorySmoothSwitchOnly);
SAFE_PARCEL(output.writeByte, frameRateSelectionStrategy);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeBool, autoRefresh);
@@ -296,6 +298,7 @@
SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
SAFE_PARCEL(input.readByte, &defaultFrameRateCompatibility);
SAFE_PARCEL(input.readByte, &frameRateCategory);
+ SAFE_PARCEL(input.readBool, &frameRateCategorySmoothSwitchOnly);
SAFE_PARCEL(input.readByte, &frameRateSelectionStrategy);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
@@ -669,6 +672,7 @@
if (other.what & eFrameRateCategoryChanged) {
what |= eFrameRateCategoryChanged;
frameRateCategory = other.frameRateCategory;
+ frameRateCategorySmoothSwitchOnly = other.frameRateCategorySmoothSwitchOnly;
}
if (other.what & eFrameRateSelectionStrategyChanged) {
what |= eFrameRateSelectionStrategyChanged;
@@ -784,7 +788,8 @@
CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
changeFrameRateStrategy);
- CHECK_DIFF(diff, eFrameRateCategoryChanged, other, frameRateCategory);
+ CHECK_DIFF2(diff, eFrameRateCategoryChanged, other, frameRateCategory,
+ frameRateCategorySmoothSwitchOnly);
CHECK_DIFF(diff, eFrameRateSelectionStrategyChanged, other, frameRateSelectionStrategy);
CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 8a57f92..05e2ddf 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2098,7 +2098,7 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRateCategory(
- const sp<SurfaceControl>& sc, int8_t category) {
+ const sp<SurfaceControl>& sc, int8_t category, bool smoothSwitchOnly) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -2106,6 +2106,7 @@
}
s->what |= layer_state_t::eFrameRateCategoryChanged;
s->frameRateCategory = category;
+ s->frameRateCategorySmoothSwitchOnly = smoothSwitchOnly;
return *this;
}
@@ -2604,6 +2605,7 @@
outMode.xDpi = mode.xDpi;
outMode.yDpi = mode.yDpi;
outMode.refreshRate = mode.refreshRate;
+ outMode.vsyncRate = mode.vsyncRate;
outMode.appVsyncOffset = mode.appVsyncOffset;
outMode.sfVsyncOffset = mode.sfVsyncOffset;
outMode.presentationDeadline = mode.presentationDeadline;
@@ -2786,16 +2788,16 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::updateSmallAreaDetection(std::vector<int32_t>& uids,
+status_t SurfaceComposerClient::updateSmallAreaDetection(std::vector<int32_t>& appIds,
std::vector<float>& thresholds) {
binder::Status status =
- ComposerServiceAIDL::getComposerService()->updateSmallAreaDetection(uids, thresholds);
+ ComposerServiceAIDL::getComposerService()->updateSmallAreaDetection(appIds, thresholds);
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::setSmallAreaDetectionThreshold(uid_t uid, float threshold) {
+status_t SurfaceComposerClient::setSmallAreaDetectionThreshold(int32_t appId, float threshold) {
binder::Status status =
- ComposerServiceAIDL::getComposerService()->setSmallAreaDetectionThreshold(uid,
+ ComposerServiceAIDL::getComposerService()->setSmallAreaDetectionThreshold(appId,
threshold);
return statusTFromBinderStatus(status);
}
diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl
index ce30426..b057653 100644
--- a/libs/gui/aidl/android/gui/DisplayMode.aidl
+++ b/libs/gui/aidl/android/gui/DisplayMode.aidl
@@ -29,7 +29,9 @@
float yDpi = 0.0f;
int[] supportedHdrTypes;
+ // Some modes have peak refresh rate lower than the panel vsync rate.
float refreshRate = 0.0f;
+ float vsyncRate = 0.0f;
long appVsyncOffset = 0;
long sfVsyncOffset = 0;
long presentationDeadline = 0;
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 507e086..a7cf5dd 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -480,14 +480,14 @@
*/
void setOverrideFrameRate(int uid, float frameRate);
- oneway void updateSmallAreaDetection(in int[] uids, in float[] thresholds);
+ oneway void updateSmallAreaDetection(in int[] appIds, in float[] thresholds);
/**
- * Set the small area detection threshold for a specified uid by SmallAreaDetectionController.
- * Passing the threshold and uid to SurfaceFlinger to update the uid-threshold mapping
+ * Set the small area detection threshold for a specified appId by SmallAreaDetectionController.
+ * Passing the threshold and appId to SurfaceFlinger to update the appId-threshold mapping
* in the scheduler.
*/
- oneway void setSmallAreaDetectionThreshold(int uid, float threshold);
+ oneway void setSmallAreaDetectionThreshold(int appId, float threshold);
/**
* Enables or disables the frame rate overlay in the top left corner.
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 39bcb4a..364a57b 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -120,14 +120,17 @@
status_t readFromParcel(const Parcel* input) override;
JankData();
- JankData(int64_t frameVsyncId, int32_t jankType)
- : frameVsyncId(frameVsyncId), jankType(jankType) {}
+ JankData(int64_t frameVsyncId, int32_t jankType, nsecs_t frameIntervalNs)
+ : frameVsyncId(frameVsyncId), jankType(jankType), frameIntervalNs(frameIntervalNs) {}
// Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
int64_t frameVsyncId;
// Bitmask of janks that occurred
int32_t jankType;
+
+ // Expected duration of the frame
+ nsecs_t frameIntervalNs;
};
class SurfaceStats : public Parcelable {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 4371007..e1dc791 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -277,8 +277,8 @@
layer_state_t::eLayerStackChanged;
// Changes that affect the visible region on a display.
- static constexpr uint64_t VISIBLE_REGION_CHANGES =
- layer_state_t::GEOMETRY_CHANGES | layer_state_t::HIERARCHY_CHANGES;
+ static constexpr uint64_t VISIBLE_REGION_CHANGES = layer_state_t::GEOMETRY_CHANGES |
+ layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged;
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
@@ -361,6 +361,7 @@
// Frame rate category to suggest what frame rate range a surface should run.
int8_t frameRateCategory;
+ bool frameRateCategorySmoothSwitchOnly;
// Strategy of the layer for frame rate selection.
int8_t frameRateSelectionStrategy;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 26b1fbd..54c3aa7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -203,15 +203,15 @@
// by GameManager.
static status_t setOverrideFrameRate(uid_t uid, float frameRate);
- // Update the small area detection whole uid-threshold mappings by same size uid and threshold
- // vector.
+ // Update the small area detection whole appId-threshold mappings by same size appId and
+ // threshold vector.
// Ref:setSmallAreaDetectionThreshold.
- static status_t updateSmallAreaDetection(std::vector<int32_t>& uids,
+ static status_t updateSmallAreaDetection(std::vector<int32_t>& appIds,
std::vector<float>& thresholds);
- // Sets the small area detection threshold to particular apps (uid). Passing value 0 means
+ // Sets the small area detection threshold to particular apps (appId). Passing value 0 means
// to disable small area detection to the app.
- static status_t setSmallAreaDetectionThreshold(uid_t uid, float threshold);
+ static status_t setSmallAreaDetectionThreshold(int32_t appId, float threshold);
// Switches on/off Auto Low Latency Mode on the connected display. This should only be
// called if the connected display supports Auto Low Latency Mode as reported by
@@ -686,7 +686,8 @@
Transaction& setDefaultFrameRateCompatibility(const sp<SurfaceControl>& sc,
int8_t compatibility);
- Transaction& setFrameRateCategory(const sp<SurfaceControl>& sc, int8_t category);
+ Transaction& setFrameRateCategory(const sp<SurfaceControl>& sc, int8_t category,
+ bool smoothSwitchOnly);
Transaction& setFrameRateSelectionStrategy(const sp<SurfaceControl>& sc, int8_t strategy);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 9eee699..e7b1232 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -999,12 +999,12 @@
binder::Status scheduleCommit() override { return binder::Status::ok(); }
- binder::Status updateSmallAreaDetection(const std::vector<int32_t>& /*uids*/,
+ binder::Status updateSmallAreaDetection(const std::vector<int32_t>& /*appIds*/,
const std::vector<float>& /*thresholds*/) {
return binder::Status::ok();
}
- binder::Status setSmallAreaDetectionThreshold(int32_t /*uid*/, float /*threshold*/) {
+ binder::Status setSmallAreaDetectionThreshold(int32_t /*appId*/, float /*threshold*/) {
return binder::Status::ok();
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index ab4af1a..69a4f0a 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -46,6 +46,14 @@
name: "aconfig_input_flags_c_lib",
aconfig_declarations: "aconfig_input_flags",
host_supported: true,
+ // Use the test version of the aconfig flag library by default to allow tests to set local
+ // overrides for flags, without having to link against a separate version of libinput or of this
+ // library. Bundling this library directly into libinput prevents us from having to add this
+ // library as a shared lib dependency everywhere where libinput is used.
+ test: true,
+ shared: {
+ enabled: false,
+ },
}
aidl_interface {
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c127411..bd5b67b 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -217,6 +217,19 @@
return toolType == ToolType::STYLUS || toolType == ToolType::ERASER;
}
+bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& properties) {
+ if (!isFromSource(source, AINPUT_SOURCE_STYLUS)) {
+ return false;
+ }
+ // Need at least one stylus pointer for this event to be considered a stylus event
+ for (const PointerProperties& pointerProperties : properties) {
+ if (isStylusToolType(pointerProperties.toolType)) {
+ return true;
+ }
+ }
+ return false;
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 804f96d..705c959 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -180,6 +180,7 @@
bitflags! {
/// MotionEvent flags.
+ #[derive(Debug)]
pub struct MotionFlags: u32 {
/// FLAG_CANCELED
const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED as u32;
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 2d94e70..bbc6d98 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -22,6 +22,49 @@
use std::collections::HashMap;
use std::collections::HashSet;
+fn verify_event(
+ action: MotionAction,
+ pointer_properties: &[RustPointerProperties],
+ flags: &MotionFlags,
+) -> Result<(), String> {
+ let pointer_count = pointer_properties.len();
+ if pointer_count < 1 {
+ return Err(format!("Invalid {} event: no pointers", action));
+ }
+ match action {
+ MotionAction::Down
+ | MotionAction::HoverEnter
+ | MotionAction::HoverExit
+ | MotionAction::HoverMove
+ | MotionAction::Up => {
+ if pointer_count != 1 {
+ return Err(format!(
+ "Invalid {} event: there are {} pointers in the event",
+ action, pointer_count
+ ));
+ }
+ }
+
+ MotionAction::Cancel => {
+ if !flags.contains(MotionFlags::CANCELED) {
+ return Err(format!(
+ "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {:#?}",
+ flags
+ ));
+ }
+ }
+
+ MotionAction::PointerDown { action_index } | MotionAction::PointerUp { action_index } => {
+ if action_index >= pointer_count {
+ return Err(format!("Got {}, but event has {} pointer(s)", action, pointer_count));
+ }
+ }
+
+ _ => {}
+ }
+ Ok(())
+}
+
/// The InputVerifier is used to validate a stream of input events.
pub struct InputVerifier {
name: String,
@@ -71,15 +114,10 @@
);
}
+ verify_event(action.into(), pointer_properties, &flags)?;
+
match action.into() {
MotionAction::Down => {
- if pointer_properties.len() != 1 {
- return Err(format!(
- "{}: Invalid DOWN event: there are {} pointers in the event",
- self.name,
- pointer_properties.len()
- ));
- }
let it = self
.touching_pointer_ids_by_device
.entry(device_id)
@@ -140,13 +178,6 @@
it.remove(&pointer_id);
}
MotionAction::Up => {
- if pointer_properties.len() != 1 {
- return Err(format!(
- "{}: Invalid UP event: there are {} pointers in the event",
- self.name,
- pointer_properties.len()
- ));
- }
if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
return Err(format!(
"{} Received ACTION_UP but no pointers are currently down for device {:?}",
@@ -171,12 +202,6 @@
self.touching_pointer_ids_by_device.remove(&device_id);
}
MotionAction::Cancel => {
- if !flags.contains(MotionFlags::CANCELED) {
- return Err(format!(
- "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
- self.name
- ));
- }
if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
return Err(format!(
"{}: Got ACTION_CANCEL, but the pointers don't match. \
@@ -274,6 +299,26 @@
use crate::MotionFlags;
use crate::RustPointerProperties;
use crate::Source;
+
+ #[test]
+ /**
+ * Send a DOWN event with 2 pointers and ensure that it's marked as invalid.
+ */
+ fn bad_down_event() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ true);
+ let pointer_properties =
+ Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
+
#[test]
fn single_pointer_stream() {
let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index dc1575c..90d0a8e 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -19,7 +19,7 @@
}
rust_bindgen {
- name: "libnativewindow_bindgen",
+ name: "libnativewindow_bindgen_internal",
crate_name: "nativewindow_bindgen",
wrapper_src: "sys/nativewindow_bindings.h",
source_stem: "bindings",
@@ -28,13 +28,21 @@
"--bitfield-enum=AHardwareBuffer_UsageFlags",
"--allowlist-file=.*/nativewindow/include/.*\\.h",
+ "--blocklist-type",
+ "AParcel",
+ "--raw-line",
+ "use binder::unstable_api::AParcel;",
"--with-derive-eq",
"--with-derive-partialeq",
],
shared_libs: [
+ "libbinder_ndk",
"libnativewindow",
],
+ rustlibs: [
+ "libbinder_rs",
+ ],
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
@@ -44,12 +52,40 @@
},
},
min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
+}
+
+rust_library {
+ name: "libnativewindow_bindgen",
+ crate_name: "nativewindow_bindgen",
+ srcs: [":libnativewindow_bindgen_internal"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libnativewindow",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+ lints: "none",
+ clippy_lints: "none",
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
}
rust_test {
name: "libnativewindow_bindgen_test",
- srcs: [":libnativewindow_bindgen"],
+ srcs: [":libnativewindow_bindgen_internal"],
crate_name: "nativewindow_bindgen_test",
+ rustlibs: [
+ "libbinder_rs",
+ ],
test_suites: ["general-tests"],
auto_gen_config: true,
clippy_lints: "none",
@@ -60,6 +96,7 @@
name: "libnativewindow_defaults",
srcs: ["src/lib.rs"],
rustlibs: [
+ "libbinder_rs",
"libnativewindow_bindgen",
],
}
@@ -77,6 +114,7 @@
},
},
min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
}
rust_test {
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 6eb3bbc..6f86c4a 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,13 +16,22 @@
extern crate nativewindow_bindgen as ffi;
-pub use ffi::{AHardwareBuffer, AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+use binder::{
+ binder_impl::{
+ BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize,
+ SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
+ },
+ unstable_api::{status_result, AsNative},
+ StatusCode,
+};
+use ffi::{AHardwareBuffer, AHardwareBuffer_readFromParcel, AHardwareBuffer_writeToParcel};
use std::fmt::{self, Debug, Formatter};
use std::mem::ManuallyDrop;
-use std::ptr::{self, NonNull};
+use std::ptr::{self, null_mut, NonNull};
-/// Wrapper around an opaque C AHardwareBuffer.
+/// Wrapper around an opaque C `AHardwareBuffer`.
#[derive(PartialEq, Eq)]
pub struct HardwareBuffer(NonNull<AHardwareBuffer>);
@@ -120,8 +129,11 @@
/// Available since API level 31.
pub fn id(&self) -> u64 {
let mut out_id = 0;
- // SAFETY: Neither pointers can be null.
- let status = unsafe { ffi::AHardwareBuffer_getId(self.0.as_ref(), &mut out_id) };
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it. The id pointer must be valid because it comes from a reference.
+ let status = unsafe { ffi::AHardwareBuffer_getId(self.0.as_ptr(), &mut out_id) };
assert_eq!(status, 0, "id() failed for AHardwareBuffer with error code: {status}");
out_id
@@ -176,9 +188,10 @@
impl Drop for HardwareBuffer {
fn drop(&mut self) {
- // SAFETY: self.0 will never be null. AHardwareBuffers allocated from within Rust will have
- // a refcount of one, and there is a safety warning on taking an AHardwareBuffer from a raw
- // pointer requiring callers to ensure the refcount is managed appropriately.
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
unsafe { ffi::AHardwareBuffer_release(self.0.as_ptr()) }
}
}
@@ -197,6 +210,82 @@
}
}
+impl Serialize for HardwareBuffer {
+ fn serialize(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ SerializeOption::serialize_option(Some(self), parcel)
+ }
+}
+
+impl SerializeOption for HardwareBuffer {
+ fn serialize_option(
+ this: Option<&Self>,
+ parcel: &mut BorrowedParcel,
+ ) -> Result<(), StatusCode> {
+ if let Some(this) = this {
+ parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
+
+ let status =
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
+ unsafe { AHardwareBuffer_writeToParcel(this.0.as_ptr(), parcel.as_native_mut()) };
+ status_result(status)
+ } else {
+ parcel.write(&NULL_PARCELABLE_FLAG)
+ }
+ }
+}
+
+impl Deserialize for HardwareBuffer {
+ type UninitType = Option<Self>;
+
+ fn uninit() -> Option<Self> {
+ None
+ }
+
+ fn from_init(value: Self) -> Option<Self> {
+ Some(value)
+ }
+
+ fn deserialize(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ DeserializeOption::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl DeserializeOption for HardwareBuffer {
+ fn deserialize_option(parcel: &BorrowedParcel) -> Result<Option<Self>, StatusCode> {
+ let present: i32 = parcel.read()?;
+ match present {
+ NULL_PARCELABLE_FLAG => Ok(None),
+ NON_NULL_PARCELABLE_FLAG => {
+ let mut buffer = null_mut();
+
+ let status =
+ // SAFETY: Both pointers must be valid because they are obtained from references.
+ // `AHardwareBuffer_readFromParcel` doesn't store them or do anything else special
+ // with them. If it returns success then it will have allocated a new
+ // `AHardwareBuffer` and incremented the reference count, so we can use it until we
+ // release it.
+ unsafe { AHardwareBuffer_readFromParcel(parcel.as_native(), &mut buffer) };
+
+ status_result(status)?;
+
+ Ok(Some(Self(NonNull::new(buffer).expect(
+ "AHardwareBuffer_readFromParcel returned success but didn't allocate buffer",
+ ))))
+ }
+ _ => Err(StatusCode::BAD_VALUE),
+ }
+ }
+}
+
+impl SerializeArray for HardwareBuffer {}
+
+impl DeserializeArray for HardwareBuffer {}
+
// SAFETY: The underlying *AHardwareBuffers can be moved between threads.
unsafe impl Send for HardwareBuffer {}
diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
index e652aee..4525a42 100644
--- a/libs/nativewindow/rust/sys/nativewindow_bindings.h
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -16,5 +16,6 @@
#include <android/data_space.h>
#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_aidl.h>
#include <android/hdr_metadata.h>
#include <android/native_window.h>
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index b992d82..818d035 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -107,7 +107,7 @@
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
- virtual std::future<void> primeCache() = 0;
+ virtual std::future<void> primeCache(bool shouldPrimeUltraHDR) = 0;
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 160006d..a58a65c 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -33,7 +33,7 @@
RenderEngine();
~RenderEngine() override;
- MOCK_METHOD0(primeCache, std::future<void>());
+ MOCK_METHOD1(primeCache, std::future<void>(bool));
MOCK_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 9d61d62..abe0d9b 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -17,6 +17,7 @@
#include "AutoBackendTexture.h"
#include "SkiaRenderEngine.h"
#include "android-base/unique_fd.h"
+#include "cutils/properties.h"
#include "renderengine/DisplaySettings.h"
#include "renderengine/LayerSettings.h"
#include "renderengine/impl/ExternalTexture.h"
@@ -49,6 +50,15 @@
// a color correction effect is added to the shader.
constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3;
+constexpr auto kBT2020DataSpace = ui::Dataspace::BT2020_ITU_PQ;
+constexpr auto kExtendedHdrDataSpce =
+ static_cast<ui::Dataspace>(ui::Dataspace::RANGE_EXTENDED | ui::Dataspace::TRANSFER_SRGB |
+ ui::Dataspace::STANDARD_DCI_P3);
+// Dimming is needed to trigger linear effects for some dataspace pairs
+const std::array<float, 3> kLayerWhitePoints = {
+ 1000.0f, 500.0f,
+ 100.0f, // trigger dithering by dimming below 20%
+};
} // namespace
static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -317,6 +327,298 @@
renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
+static void drawImageDimmedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ // The position transform doesn't matter when the reduced shader mode
+ // in in effect. A matrix transform stage is always included.
+ .positionTransform = mat4(),
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ .roundedCornersRadius = {0.f, 0.f},
+ },
+ .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
+ .maxLuminanceNits = 1000.f,
+ .usePremultipliedAlpha = true,
+ .isOpaque = true}},
+ .alpha = 1.f,
+ .sourceDataspace = kDestDataSpace,
+ };
+
+ std::vector<LayerSettings> layers;
+
+ for (auto layerWhitePoint : kLayerWhitePoints) {
+ layer.whitePointNits = layerWhitePoint;
+ layers.push_back(layer);
+ }
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+}
+
+static void drawTransparentImageDimmedLayers(SkiaRenderEngine* renderengine,
+ const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .positionTransform = mat4(),
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .maxLuminanceNits = 1000.f,
+ .usePremultipliedAlpha = true,
+ .isOpaque = false,
+ }},
+ .sourceDataspace = kDestDataSpace,
+ };
+
+ for (auto roundedCornerRadius : {0.f, 50.f}) {
+ layer.geometry.roundedCornersRadius = {roundedCornerRadius, roundedCornerRadius};
+ for (auto alpha : {0.5f, 1.0f}) {
+ layer.alpha = alpha;
+ for (auto isOpaque : {true, false}) {
+ if (roundedCornerRadius == 0.f && isOpaque) {
+ // already covered in drawImageDimmedLayers
+ continue;
+ }
+
+ layer.source.buffer.isOpaque = isOpaque;
+ std::vector<LayerSettings> layers;
+
+ for (auto layerWhitePoint : kLayerWhitePoints) {
+ layer.whitePointNits = layerWhitePoint;
+ layers.push_back(layer);
+ }
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+ }
+ }
+ }
+}
+
+static void drawClippedDimmedImageLayers(SkiaRenderEngine* renderengine,
+ const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+
+ // If rect and boundary is too small compared to roundedCornersRadius, Skia will switch to
+ // blending instead of EllipticalRRect, so enlarge them a bit.
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ FloatRect boundary(0, 0, displayRect.width(),
+ displayRect.height() - 20); // boundary is smaller
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .positionTransform = mat4(),
+ .boundaries = boundary,
+ .roundedCornersCrop = rect,
+ .roundedCornersRadius = {27.f, 27.f},
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .maxLuminanceNits = 1000.f,
+ .usePremultipliedAlpha = true,
+ .isOpaque = false,
+ }},
+ .alpha = 1.f,
+ .sourceDataspace = kDestDataSpace,
+ };
+
+ std::array<mat4, 2> transforms = {kScaleAndTranslate, kScaleAsymmetric};
+
+ constexpr float radius = 27.f;
+
+ for (size_t i = 0; i < transforms.size(); i++) {
+ layer.geometry.positionTransform = transforms[i];
+ layer.geometry.roundedCornersRadius = {radius, radius};
+
+ std::vector<LayerSettings> layers;
+
+ for (auto layerWhitePoint : kLayerWhitePoints) {
+ layer.whitePointNits = layerWhitePoint;
+ layers.push_back(layer);
+ }
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+ }
+}
+
+static void drawSolidDimmedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ },
+ .source =
+ PixelSource{
+ .solidColor = half3(0.1f, 0.2f, 0.3f),
+ },
+ .alpha = 1.f,
+ };
+
+ std::vector<LayerSettings> layers;
+
+ for (auto layerWhitePoint : kLayerWhitePoints) {
+ layer.whitePointNits = layerWhitePoint;
+ layers.push_back(layer);
+ }
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+}
+
+static void drawBT2020ImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ // The position transform doesn't matter when the reduced shader mode
+ // in in effect. A matrix transform stage is always included.
+ .positionTransform = mat4(),
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ .roundedCornersRadius = {0.f, 0.f},
+ },
+ .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
+ .maxLuminanceNits = 1000.f,
+ .usePremultipliedAlpha = true,
+ .isOpaque = true}},
+ .alpha = 1.f,
+ .sourceDataspace = kBT2020DataSpace,
+ };
+
+ for (auto alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha;
+ std::vector<LayerSettings> layers;
+ layer.whitePointNits = -1.f;
+ layers.push_back(layer);
+
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+ }
+}
+static void drawBT2020ClippedImageLayers(SkiaRenderEngine* renderengine,
+ const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+
+ // If rect and boundary is too small compared to roundedCornersRadius, Skia will switch to
+ // blending instead of EllipticalRRect, so enlarge them a bit.
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ FloatRect boundary(0, 0, displayRect.width(),
+ displayRect.height() - 10); // boundary is smaller
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .positionTransform = kScaleAsymmetric,
+ .boundaries = boundary,
+ .roundedCornersCrop = rect,
+ .roundedCornersRadius = {64.1f, 64.1f},
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .maxLuminanceNits = 1000.f,
+ .usePremultipliedAlpha = true,
+ .isOpaque = true,
+ }},
+ .alpha = 0.5f,
+ .sourceDataspace = kBT2020DataSpace,
+ };
+
+ std::vector<LayerSettings> layers = {layer};
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+}
+
+static void drawExtendedHDRImageLayers(SkiaRenderEngine* renderengine,
+ const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ // The position transform doesn't matter when the reduced shader mode
+ // in in effect. A matrix transform stage is always included.
+ .positionTransform = mat4(),
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ .roundedCornersRadius = {50.f, 50.f},
+ },
+ .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
+ .maxLuminanceNits = 1000.f,
+ .usePremultipliedAlpha = true,
+ .isOpaque = true}},
+ .alpha = 0.5f,
+ .sourceDataspace = kExtendedHdrDataSpce,
+ };
+
+ for (auto roundedCornerRadius : {0.f, 50.f}) {
+ layer.geometry.roundedCornersRadius = {roundedCornerRadius, roundedCornerRadius};
+ for (auto alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha;
+ std::vector<LayerSettings> layers;
+
+ for (auto layerWhitePoint : kLayerWhitePoints) {
+ layer.whitePointNits = layerWhitePoint;
+ layers.push_back(layer);
+ }
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+ }
+ }
+}
+
+static void drawP3ImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ // The position transform doesn't matter when the reduced shader mode
+ // in in effect. A matrix transform stage is always included.
+ .positionTransform = mat4(),
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ .roundedCornersRadius = {50.f, 50.f},
+ },
+ .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
+ .maxLuminanceNits = 1000.f,
+ .usePremultipliedAlpha = true,
+ .isOpaque = false}},
+ .alpha = 0.5f,
+ .sourceDataspace = kOtherDataSpace,
+ };
+
+ for (auto alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha;
+ std::vector<LayerSettings> layers;
+
+ for (auto layerWhitePoint : kLayerWhitePoints) {
+ layer.whitePointNits = layerWhitePoint;
+ layers.push_back(layer);
+ }
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+ }
+}
+
//
// The collection of shaders cached here were found by using perfetto to record shader compiles
// during actions that involve RenderEngine, logging the layer settings, and the shader code
@@ -328,7 +630,7 @@
// kFlushAfterEveryLayer = true
// in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
// gPrintSKSL = true
-void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine, bool shouldPrimeUltraHDR) {
const int previousCount = renderengine->reportShadersCompiled();
if (previousCount) {
ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
@@ -353,6 +655,23 @@
.maxLuminance = 500,
.outputDataspace = kOtherDataSpace,
};
+ DisplaySettings p3DisplayEnhance{.physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = kOtherDataSpace,
+ .dimmingStage = aidl::android::hardware::graphics::
+ composer3::DimmingStage::GAMMA_OETF,
+ .renderIntent = aidl::android::hardware::graphics::
+ composer3::RenderIntent::ENHANCE};
+ DisplaySettings bt2020Display{.physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = ui::Dataspace::BT2020,
+ .deviceHandlesColorTransform = true,
+ .dimmingStage = aidl::android::hardware::graphics::composer3::
+ DimmingStage::GAMMA_OETF,
+ .renderIntent = aidl::android::hardware::graphics::composer3::
+ RenderIntent::TONE_MAP_ENHANCE};
const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -377,6 +696,8 @@
impl::ExternalTexture::Usage::WRITEABLE);
drawHolePunchLayer(renderengine, display, dstTexture);
drawSolidLayers(renderengine, display, dstTexture);
+ drawSolidLayers(renderengine, p3Display, dstTexture);
+ drawSolidDimmedLayers(renderengine, display, dstTexture);
drawShadowLayers(renderengine, display, srcTexture);
drawShadowLayers(renderengine, p3Display, srcTexture);
@@ -417,12 +738,38 @@
for (auto texture : textures) {
drawImageLayers(renderengine, display, dstTexture, texture);
+
+ drawImageDimmedLayers(renderengine, display, dstTexture, texture);
+ drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture);
+ drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture);
+
// Draw layers for b/185569240.
drawClippedLayers(renderengine, display, dstTexture, texture);
}
drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
+ drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture, externalTexture);
+ drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture);
+ drawTransparentImageDimmedLayers(renderengine, p3Display, dstTexture, externalTexture);
+ drawTransparentImageDimmedLayers(renderengine, p3DisplayEnhance, dstTexture,
+ externalTexture);
+
+ drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
+
+ if (shouldPrimeUltraHDR) {
+ drawBT2020ClippedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
+
+ drawBT2020ImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
+ drawBT2020ImageLayers(renderengine, p3Display, dstTexture, externalTexture);
+
+ drawExtendedHDRImageLayers(renderengine, display, dstTexture, externalTexture);
+ drawExtendedHDRImageLayers(renderengine, p3Display, dstTexture, externalTexture);
+ drawExtendedHDRImageLayers(renderengine, p3DisplayEnhance, dstTexture, externalTexture);
+
+ drawP3ImageLayers(renderengine, p3DisplayEnhance, dstTexture, externalTexture);
+ }
+
// draw one final layer synchronously to force GL submit
LayerSettings layer{
.source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)},
diff --git a/libs/renderengine/skia/Cache.h b/libs/renderengine/skia/Cache.h
index 437571e..62f6705 100644
--- a/libs/renderengine/skia/Cache.h
+++ b/libs/renderengine/skia/Cache.h
@@ -22,7 +22,7 @@
class Cache {
public:
- static void primeShaderCache(SkiaRenderEngine*);
+ static void primeShaderCache(SkiaRenderEngine*, bool shouldPrimeUltraHDR);
private:
Cache() = default;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 7ec98ea..3729be6 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -243,8 +243,8 @@
using base::StringAppendF;
-std::future<void> SkiaRenderEngine::primeCache() {
- Cache::primeShaderCache(this);
+std::future<void> SkiaRenderEngine::primeCache(bool shouldPrimeUltraHDR) {
+ Cache::primeShaderCache(this, shouldPrimeUltraHDR);
return {};
}
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 3db0c1b..ac134af 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -62,7 +62,7 @@
SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur);
~SkiaRenderEngine() override;
- std::future<void> primeCache() override final;
+ std::future<void> primeCache(bool shouldPrimeUltraHDR) override final;
void cleanupPostRender() override final;
bool supportsBackgroundBlur() override final {
return mBlurFilter != nullptr;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 17f263d..8821c0e 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -25,6 +25,7 @@
#include <GrContextOptions.h>
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
+#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <android-base/stringprintf.h>
#include <gui/TraceUtils.h>
@@ -603,11 +604,11 @@
sSetupVulkanInterface();
SkiaRenderEngine::Contexts contexts;
- contexts.first = GrDirectContext::MakeVulkan(sVulkanInterface.getBackendContext(), options);
+ contexts.first = GrDirectContexts::MakeVulkan(sVulkanInterface.getBackendContext(), options);
if (supportsProtectedContentImpl()) {
contexts.second =
- GrDirectContext::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(),
- options);
+ GrDirectContexts::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(),
+ options);
}
return contexts;
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
index f24a4f1..5bf6560 100644
--- a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
@@ -18,7 +18,6 @@
#include "SkiaMemoryReporter.h"
-#include <SkString.h>
#include <android-base/stringprintf.h>
#include <log/log_main.h>
@@ -142,7 +141,7 @@
TraceValue traceValue = convertUnits(result->second);
const char* entry = (traceValue.count > 1) ? "entries" : "entry";
StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
- traceValue.value, traceValue.units, traceValue.count, entry);
+ traceValue.value, traceValue.units.c_str(), traceValue.count, entry);
}
if (mItemize) {
for (const auto& individualItem : resultsMap) {
@@ -153,7 +152,7 @@
auto result = individualItem.second.find("size");
TraceValue size = convertUnits(result->second);
StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(),
- size.value, size.units);
+ size.value, size.units.c_str());
if (!wrappedResources) {
for (const auto& itemValues : individualItem.second) {
if (strcmp("size", itemValues.first) == 0) {
@@ -162,10 +161,10 @@
TraceValue traceValue = convertUnits(itemValues.second);
if (traceValue.value == 0.0f) {
StringAppendF(&log, " %s[%s]", itemValues.first,
- traceValue.units);
+ traceValue.units.c_str());
} else {
StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
- traceValue.value, traceValue.units);
+ traceValue.value, traceValue.units.c_str());
}
}
}
@@ -184,16 +183,16 @@
TraceValue total = convertUnits(mTotalSize);
TraceValue purgeable = convertUnits(mPurgeableSize);
StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
- total.value, total.units, purgeable.value, purgeable.units);
+ total.value, total.units.c_str(), purgeable.value, purgeable.units.c_str());
}
SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
TraceValue output(value);
- if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
+ if (SkString("bytes") == output.units && output.value >= 1024) {
output.value = output.value / 1024.0f;
output.units = "KB";
}
- if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
+ if (SkString("KB") == output.units && output.value >= 1024) {
output.value = output.value / 1024.0f;
output.units = "MB";
}
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.h b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
index dbbd65b..da91674 100644
--- a/libs/renderengine/skia/debug/SkiaMemoryReporter.h
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
@@ -16,6 +16,7 @@
#pragma once
+#include <SkString.h>
#include <SkTraceMemoryDump.h>
#include <string>
@@ -75,7 +76,7 @@
TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {}
TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {}
- const char* units;
+ SkString units;
float value;
int count;
};
@@ -104,4 +105,4 @@
} /* namespace skia */
} /* namespace renderengine */
-} /* namespace android */
\ No newline at end of file
+} /* namespace android */
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 6023808..11d4fde 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -3204,7 +3204,7 @@
}
initializeRenderEngine();
- auto fut = mRE->primeCache();
+ auto fut = mRE->primeCache(false);
if (fut.valid()) {
fut.wait();
}
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 7289fe7..1b9adba 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -50,8 +50,8 @@
}
TEST_F(RenderEngineThreadedTest, primeCache) {
- EXPECT_CALL(*mRenderEngine, primeCache());
- mThreadedRE->primeCache();
+ EXPECT_CALL(*mRenderEngine, primeCache(false));
+ mThreadedRE->primeCache(false);
// need to call ANY synchronous function after primeCache to ensure that primeCache has
// completed asynchronously before the test completes execution.
mThreadedRE->getContextPriority();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 786a6fe..367bee8 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -129,7 +129,7 @@
mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
}
-std::future<void> RenderEngineThreaded::primeCache() {
+std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) {
const auto resultPromise = std::make_shared<std::promise<void>>();
std::future<void> resultFuture = resultPromise->get_future();
ATRACE_CALL();
@@ -137,19 +137,20 @@
// for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([resultPromise](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::primeCache");
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Couldn't set SCHED_OTHER for primeCache");
- }
+ mFunctionCalls.push(
+ [resultPromise, shouldPrimeUltraHDR](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::primeCache");
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_OTHER for primeCache");
+ }
- instance.primeCache();
- resultPromise->set_value();
+ instance.primeCache(shouldPrimeUltraHDR);
+ resultPromise->set_value();
- if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Couldn't set SCHED_FIFO for primeCache");
- }
- });
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_FIFO for primeCache");
+ }
+ });
}
mCondition.notify_one();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 1093f5f..74af2bd 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
~RenderEngineThreaded() override;
- std::future<void> primeCache() override;
+ std::future<void> primeCache(bool shouldPrimeUltraHDR) override;
void dump(std::string& result) override;
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index b6b9cc4..d992aa5 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -49,6 +49,7 @@
"liblog",
"libhardware",
"libpermission",
+ "android.companion.virtual.virtualdevice_aidl-cpp",
],
export_include_dirs: ["include"],
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 634d35a..12f600e 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -43,6 +43,8 @@
CREATE_SENSOR_DIRECT_CONNECTION,
SET_OPERATION_PARAMETER,
GET_RUNTIME_SENSOR_LIST,
+ ENABLE_REPLAY_DATA_INJECTION,
+ ENABLE_HAL_BYPASS_REPLAY_DATA_INJECTION,
};
class BpSensorServer : public BpInterface<ISensorServer>
@@ -162,6 +164,20 @@
return reply.readInt32();
}
+ virtual int isReplayDataInjectionEnabled() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ remote()->transact(ENABLE_REPLAY_DATA_INJECTION, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual int isHalBypassReplayDataInjectionEnabled() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ remote()->transact(ENABLE_HAL_BYPASS_REPLAY_DATA_INJECTION, data, &reply);
+ return reply.readInt32();
+ }
+
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
int deviceId, uint32_t size, int32_t type, int32_t format,
const native_handle_t *resource) {
@@ -237,6 +253,18 @@
reply->writeInt32(static_cast<int32_t>(ret));
return NO_ERROR;
}
+ case ENABLE_REPLAY_DATA_INJECTION: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ int32_t ret = isReplayDataInjectionEnabled();
+ reply->writeInt32(static_cast<int32_t>(ret));
+ return NO_ERROR;
+ }
+ case ENABLE_HAL_BYPASS_REPLAY_DATA_INJECTION: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ int32_t ret = isHalBypassReplayDataInjectionEnabled();
+ reply->writeInt32(static_cast<int32_t>(ret));
+ return NO_ERROR;
+ }
case GET_DYNAMIC_SENSOR_LIST: {
CHECK_INTERFACE(ISensorServer, data, reply);
const String16& opPackageName = data.readString16();
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 980f8d1..d112a12 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -26,6 +26,8 @@
#include <utils/RefBase.h>
#include <utils/Singleton.h>
+#include <android/companion/virtualnative/IVirtualDeviceManagerNative.h>
+
#include <binder/IBinder.h>
#include <binder/IPermissionController.h>
#include <binder/IServiceManager.h>
@@ -39,6 +41,43 @@
namespace android {
// ----------------------------------------------------------------------------
+namespace {
+
+using ::android::companion::virtualnative::IVirtualDeviceManagerNative;
+
+static constexpr int DEVICE_ID_DEFAULT = 0;
+
+// Returns the deviceId of the device where this uid is observed. If the uid is present on more than
+// one devices, return the default deviceId.
+int getDeviceIdForUid(uid_t uid) {
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("virtualdevice_native"));
+ if (binder != nullptr) {
+ auto vdm = interface_cast<IVirtualDeviceManagerNative>(binder);
+ std::vector<int> deviceIds;
+ vdm->getDeviceIdsForUid(uid, &deviceIds);
+ // If the UID is associated with multiple virtual devices, use the default device's
+ // sensors as we cannot disambiguate here. This effectively means that the app has
+ // activities on different devices at the same time, so it must handle the device
+ // awareness by itself.
+ if (deviceIds.size() == 1) {
+ const int deviceId = deviceIds.at(0);
+ int devicePolicy = IVirtualDeviceManagerNative::DEVICE_POLICY_DEFAULT;
+ vdm->getDevicePolicy(deviceId,
+ IVirtualDeviceManagerNative::POLICY_TYPE_SENSORS,
+ &devicePolicy);
+ if (devicePolicy == IVirtualDeviceManagerNative::DEVICE_POLICY_CUSTOM) {
+ return deviceId;
+ }
+ }
+ } else {
+ ALOGW("Cannot get virtualdevice_native service");
+ }
+ return DEVICE_ID_DEFAULT;
+}
+
+} // namespace
+
Mutex SensorManager::sLock;
std::map<String16, SensorManager*> SensorManager::sPackageInstances;
@@ -53,6 +92,7 @@
sensorManager = iterator->second;
} else {
String16 opPackageName = packageName;
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
// It is possible that the calling code has no access to the package name.
// In this case we will get the packages for the calling UID and pick the
@@ -63,7 +103,6 @@
if (opPackageName.size() <= 0) {
sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
if (binder != nullptr) {
- const uid_t uid = IPCThreadState::self()->getCallingUid();
Vector<String16> packages;
interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
if (!packages.isEmpty()) {
@@ -76,7 +115,10 @@
}
}
- sensorManager = new SensorManager(opPackageName);
+ // Check if the calling UID is observed on a virtual device. If so, provide that device's
+ // sensors by default instead of the default device's sensors.
+ const int deviceId = getDeviceIdForUid(uid);
+ sensorManager = new SensorManager(opPackageName, deviceId);
// If we had no package name, we looked it up from the UID and the sensor
// manager instance we created should also be mapped to the empty package
@@ -102,8 +144,9 @@
}
}
-SensorManager::SensorManager(const String16& opPackageName)
- : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
+SensorManager::SensorManager(const String16& opPackageName, int deviceId)
+ : mSensorList(nullptr), mOpPackageName(opPackageName), mDeviceId(deviceId),
+ mDirectConnectionHandle(1) {
Mutex::Autolock _l(mLock);
assertStateLocked();
}
@@ -174,7 +217,12 @@
mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
- mSensors = mSensorServer->getSensorList(mOpPackageName);
+ if (mDeviceId == DEVICE_ID_DEFAULT) {
+ mSensors = mSensorServer->getSensorList(mOpPackageName);
+ } else {
+ mSensors = mSensorServer->getRuntimeSensorList(mOpPackageName, mDeviceId);
+ }
+
size_t count = mSensors.size();
// If count is 0, mSensorList will be non-null. This is old
// existing behavior and callers expect this.
@@ -310,6 +358,22 @@
return false;
}
+bool SensorManager::isReplayDataInjectionEnabled() {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() == NO_ERROR) {
+ return mSensorServer->isReplayDataInjectionEnabled();
+ }
+ return false;
+}
+
+bool SensorManager::isHalBypassReplayDataInjectionEnabled() {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() == NO_ERROR) {
+ return mSensorServer->isHalBypassReplayDataInjectionEnabled();
+ }
+ return false;
+}
+
int SensorManager::createDirectChannel(
size_t size, int channelType, const native_handle_t *resourceHandle) {
static constexpr int DEFAULT_DEVICE_ID = 0;
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index 5815728..00bc1d6 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -48,6 +48,8 @@
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode, const String16& opPackageName, const String16& attributionTag) = 0;
virtual int32_t isDataInjectionEnabled() = 0;
+ virtual int32_t isReplayDataInjectionEnabled() = 0;
+ virtual int32_t isHalBypassReplayDataInjectionEnabled() = 0;
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
int deviceId, uint32_t size, int32_t type, int32_t format,
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index bb44cb8..e67fac7 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -65,6 +65,8 @@
sp<SensorEventQueue> createEventQueue(
String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
bool isDataInjectionEnabled();
+ bool isReplayDataInjectionEnabled();
+ bool isHalBypassReplayDataInjectionEnabled();
int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
int createDirectChannel(
int deviceId, size_t size, int channelType, const native_handle_t *channelData);
@@ -77,7 +79,7 @@
void sensorManagerDied();
static status_t waitForSensorService(sp<ISensorServer> *server);
- explicit SensorManager(const String16& opPackageName);
+ explicit SensorManager(const String16& opPackageName, int deviceId);
status_t assertStateLocked();
private:
@@ -92,6 +94,7 @@
Vector<Sensor> mDynamicSensors;
sp<IBinder::DeathRecipient> mDeathObserver;
const String16 mOpPackageName;
+ const int mDeviceId;
std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
int32_t mDirectConnectionHandle;
};
diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h
index 65a8769..a469c78 100644
--- a/libs/ui/include/ui/DisplayMode.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -37,7 +37,9 @@
float yDpi = 0;
std::vector<ui::Hdr> supportedHdrTypes;
- float refreshRate = 0;
+ // Some modes have peak refresh rate lower than the panel vsync rate.
+ float refreshRate = 0.f;
+ float vsyncRate = 0.f;
nsecs_t appVsyncOffset = 0;
nsecs_t sfVsyncOffset = 0;
nsecs_t presentationDeadline = 0;
diff --git a/libs/vibrator/OWNERS b/libs/vibrator/OWNERS
index d073e2b..c4de58a 100644
--- a/libs/vibrator/OWNERS
+++ b/libs/vibrator/OWNERS
@@ -1 +1,2 @@
+# Bug component: 345036
include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index ee1f3cb..6f092a6 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -39,6 +39,9 @@
"options": [
{
"include-filter": "android.hardware.input.cts.tests"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index fa8f548..30e6802 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -215,8 +215,8 @@
int32_t buttonState, MotionClassification classification,
int32_t edgeFlags, float xPrecision, float yPrecision,
float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- uint32_t pointerCount, const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords)
+ const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords)
: EventEntry(id, Type::MOTION, eventTime, policyFlags),
deviceId(deviceId),
source(source),
@@ -233,12 +233,8 @@
xCursorPosition(xCursorPosition),
yCursorPosition(yCursorPosition),
downTime(downTime),
- pointerCount(pointerCount) {
- for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i] = pointerProperties[i];
- this->pointerCoords[i] = pointerCoords[i];
- }
-}
+ pointerProperties(pointerProperties),
+ pointerCoords(pointerCoords) {}
MotionEntry::~MotionEntry() {}
@@ -258,7 +254,7 @@
buttonState, motionClassificationToString(classification), edgeFlags,
xPrecision, yPrecision, xCursorPosition, yCursorPosition);
- for (uint32_t i = 0; i < pointerCount; i++) {
+ for (uint32_t i = 0; i < getPointerCount(); i++) {
if (i) {
msg += ", ";
}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 98e2507..b341784 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -175,16 +175,18 @@
float xCursorPosition;
float yCursorPosition;
nsecs_t downTime;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+
+ size_t getPointerCount() const { return pointerProperties.size(); }
MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
int32_t metaState, int32_t buttonState, MotionClassification classification,
int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
+ float yCursorPosition, nsecs_t downTime,
+ const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords);
std::string getDescription() const override;
~MotionEntry() override;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 276f75c..7dfbf94 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -365,7 +365,7 @@
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
std::vector<PointerCoords> pointerCoords;
- pointerCoords.resize(motionEntry.pointerCount);
+ pointerCoords.resize(motionEntry.getPointerCount());
// Use the first pointer information to normalize all other pointers. This could be any pointer
// as long as all other pointers are normalized to the same value and the final DispatchEntry
@@ -375,7 +375,7 @@
ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
// Iterate through all pointers in the event to normalize against the first.
- for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) {
const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId];
@@ -399,8 +399,7 @@
motionEntry.edgeFlags, motionEntry.xPrecision,
motionEntry.yPrecision, motionEntry.xCursorPosition,
motionEntry.yCursorPosition, motionEntry.downTime,
- motionEntry.pointerCount, motionEntry.pointerProperties,
- pointerCoords.data());
+ motionEntry.pointerProperties, pointerCoords);
if (motionEntry.injectionState) {
combinedMotionEntry->injectionState = motionEntry.injectionState;
@@ -635,11 +634,10 @@
const MotionEntry& entry) {
std::vector<TouchedWindow> out;
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
- if (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER &&
- maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE &&
- maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
- // Not a hover event - don't need to do anything
- return out;
+
+ if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ // ACTION_SCROLL events should not affect the hovering pointer dispatch
+ return {};
}
// We should consider all hovering pointers here. But for now, just use the first one
@@ -1316,8 +1314,9 @@
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds,
- /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
+ addPointerWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
+ pointerIds,
+ /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
}
}
return outsideTargets;
@@ -1822,7 +1821,7 @@
std::vector<InputTarget> inputTargets;
addWindowTargetLocked(focusedWindow,
InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS,
- /*pointerIds=*/{}, getDownTime(*entry), inputTargets);
+ getDownTime(*entry), inputTargets);
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
@@ -1906,7 +1905,6 @@
// Identify targets.
std::vector<InputTarget> inputTargets;
- bool conflictingPointerActions = false;
InputEventInjectionResult injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
@@ -1918,8 +1916,7 @@
}
inputTargets =
- findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
- /*byref*/ injectionResult);
+ findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult);
LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
!inputTargets.empty());
} else {
@@ -1931,7 +1928,7 @@
addWindowTargetLocked(focusedWindow,
InputTarget::Flags::FOREGROUND |
InputTarget::Flags::DISPATCH_AS_IS,
- /*pointerIds=*/{}, getDownTime(*entry), inputTargets);
+ getDownTime(*entry), inputTargets);
}
}
if (injectionResult == InputEventInjectionResult::PENDING) {
@@ -1955,11 +1952,6 @@
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
// Dispatch the motion.
- if (conflictingPointerActions) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "conflicting pointer actions");
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- }
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
@@ -2000,7 +1992,7 @@
entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision,
entry.yPrecision, entry.downTime);
- for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, "
"x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
@@ -2259,7 +2251,7 @@
}
std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+ nsecs_t currentTime, const MotionEntry& entry,
InputEventInjectionResult& outInjectionResult) {
ATRACE_CALL();
@@ -2284,20 +2276,13 @@
}
bool isSplit = shouldSplitTouch(tempTouchState, entry);
- bool switchedDevice = false;
- if (oldState != nullptr) {
- std::set<int32_t> oldActiveDevices = oldState->getActiveDeviceIds();
- const bool anotherDeviceIsActive =
- oldActiveDevices.count(entry.deviceId) == 0 && !oldActiveDevices.empty();
- switchedDevice |= anotherDeviceIsActive;
- }
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
// A DOWN could be generated from POINTER_DOWN if the initial pointers did not land into any
// touchable windows.
- const bool wasDown = oldState != nullptr && oldState->isDown();
+ const bool wasDown = oldState != nullptr && oldState->isDown(entry.deviceId);
const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
(maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
@@ -2305,34 +2290,19 @@
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
- // If pointers are already down, let's finish the current gesture and ignore the new events
- // from another device. However, if the new event is a down event, let's cancel the current
- // touch and let the new one take over.
- if (switchedDevice && wasDown && !isDown) {
- LOG(INFO) << "Dropping event because a pointer for another device "
- << " is already down in display " << displayId << ": " << entry.getDescription();
- // TODO(b/211379801): test multiple simultaneous input streams.
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {}; // wrong device
+ if (newGesture) {
+ isSplit = false;
}
- if (newGesture) {
- // If a new gesture is starting, clear the touch state completely.
- tempTouchState.reset();
- isSplit = false;
- } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
- ALOGI("Dropping move event because a pointer for a different device is already active "
- "in display %" PRId32,
- displayId);
- // TODO(b/211379801): test multiple simultaneous input streams.
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {}; // wrong device
+ if (isDown && tempTouchState.hasHoveringPointers(entry.deviceId)) {
+ // Compatibility behaviour: ACTION_DOWN causes HOVER_EXIT to get generated.
+ tempTouchState.clearHoveringPointers(entry.deviceId);
}
if (isHoverAction) {
// For hover actions, we will treat 'tempTouchState' as a new state, so let's erase
// all of the existing hovering pointers and recompute.
- tempTouchState.clearHoveringPointers();
+ tempTouchState.clearHoveringPointers(entry.deviceId);
}
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
@@ -2403,8 +2373,7 @@
continue;
}
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ if (isHoverAction) {
// The "windowHandle" is the target of this hovering pointer.
tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
}
@@ -2427,31 +2396,24 @@
}
// Update the temporary touch state.
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
+
if (!isHoverAction) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- }
-
- const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
- maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
-
- // TODO(b/211379801): Currently, even if pointerIds are empty (hover case), we would
- // still add a window to the touch state. We should avoid doing that, but some of the
- // later checks ("at least one foreground window") rely on this in order to dispatch
- // the event properly, so that needs to be updated, possibly by looking at InputTargets.
- tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds,
- isDownOrPointerDown
- ? std::make_optional(entry.eventTime)
- : std::nullopt);
-
- // If this is the pointer going down and the touched window has a wallpaper
- // then also add the touched wallpaper windows so they are locked in for the duration
- // of the touch gesture.
- // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
- // engine only supports touch events. We would need to add a mechanism similar
- // to View.onGenericMotionEvent to enable wallpapers to handle these events.
- if (isDownOrPointerDown) {
- if (targetFlags.test(InputTarget::Flags::FOREGROUND) &&
+ const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+ maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
+ tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId,
+ pointerIds,
+ isDownOrPointerDown
+ ? std::make_optional(entry.eventTime)
+ : std::nullopt);
+ // If this is the pointer going down and the touched window has a wallpaper
+ // then also add the touched wallpaper windows so they are locked in for the
+ // duration of the touch gesture. We do not collect wallpapers during HOVER_MOVE or
+ // SCROLL because the wallpaper engine only supports touch events. We would need to
+ // add a mechanism similar to View.onGenericMotionEvent to enable wallpapers to
+ // handle these events.
+ if (isDownOrPointerDown && targetFlags.test(InputTarget::Flags::FOREGROUND) &&
windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
@@ -2489,8 +2451,10 @@
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (!tempTouchState.isDown() && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
- LOG(INFO) << "Dropping event because the pointer is not down or we previously "
+ if (!tempTouchState.isDown(entry.deviceId) &&
+ maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
+ << " is not down or we previously "
"dropped the pointer down event in display "
<< displayId << ": " << entry.getDescription();
outInjectionResult = InputEventInjectionResult::FAILED;
@@ -2514,7 +2478,7 @@
addDragEventLocked(entry);
// Check whether touches should slip outside of the current foreground window.
- if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.getPointerCount() == 1 &&
tempTouchState.isSlippery()) {
const auto [x, y] = resolveTouchedPosition(entry);
const bool isStylus = isPointerFromStylus(entry, /*pointerIndex=*/0);
@@ -2539,7 +2503,7 @@
if (newTouchedWindowHandle != nullptr &&
!haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) {
- ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
+ ALOGI("Touch is slipping out of window %s into window %s in display %" PRId32,
oldTouchedWindowHandle->getName().c_str(),
newTouchedWindowHandle->getName().c_str(), displayId);
@@ -2550,9 +2514,11 @@
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
- addWindowTargetLocked(oldTouchedWindowHandle,
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds,
- touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
+ addPointerWindowTargetLocked(oldTouchedWindowHandle,
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
+ pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ targets);
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2665,16 +2631,14 @@
// Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (!touchedWindow.hasTouchingPointers(entry.deviceId) &&
- !touchedWindow.hasHoveringPointers(entry.deviceId)) {
- // Windows with hovering pointers are getting persisted inside TouchState.
- // Do not send this event to those windows.
+ std::bitset<MAX_POINTER_ID + 1> touchingPointers =
+ touchedWindow.getTouchingPointers(entry.deviceId);
+ if (touchingPointers.none()) {
continue;
}
-
- addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.getTouchingPointers(entry.deviceId),
- touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
+ addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+ touchingPointers,
+ touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
}
// During targeted injection, only allow owned targets to receive events
@@ -2707,37 +2671,30 @@
}
outInjectionResult = InputEventInjectionResult::SUCCEEDED;
- // Drop the outside or hover touch windows since we will not care about them
- // in the next iteration.
- tempTouchState.filterNonAsIsTouchWindows();
- // Update final pieces of touch state if the injector had permission.
- if (switchedDevice) {
- if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Switched to a different device.");
+ for (TouchedWindow& touchedWindow : tempTouchState.windows) {
+ // Targets that we entered in a slippery way will now become AS-IS targets
+ if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
+ touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER);
+ touchedWindow.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS;
}
- *outConflictingPointerActions = true;
}
+ // Update final pieces of touch state if the injector had permission.
if (isHoverAction) {
- // Started hovering, therefore no longer down.
- if (oldState && oldState->isDown()) {
- ALOGD_IF(DEBUG_FOCUS,
- "Conflicting pointer actions: Hover received while pointer was down.");
- *outConflictingPointerActions = true;
+ if (oldState && oldState->isDown(entry.deviceId)) {
+ // Started hovering, but the device is already down: reject the hover event
+ LOG(ERROR) << "Got hover event " << entry.getDescription()
+ << " but the device is already down " << oldState->dump();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
}
} else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
// Pointer went up.
tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id);
} else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
- tempTouchState.reset();
- } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- // First pointer went down.
- if (oldState && (oldState->isDown() || oldState->hasHoveringPointers())) {
- ALOGD("Conflicting pointer actions: Down received while already down or hovering.");
- *outConflictingPointerActions = true;
- }
+ tempTouchState.removeAllPointersForDevice(entry.deviceId);
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
// One pointer went up.
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
@@ -2793,14 +2750,14 @@
// Find the pointer index by id.
int32_t pointerIndex = 0;
- for (; static_cast<uint32_t>(pointerIndex) < entry.pointerCount; pointerIndex++) {
+ for (; static_cast<uint32_t>(pointerIndex) < entry.getPointerCount(); pointerIndex++) {
const PointerProperties& pointerProperties = entry.pointerProperties[pointerIndex];
if (pointerProperties.id == mDragState->pointerId) {
break;
}
}
- if (uint32_t(pointerIndex) == entry.pointerCount) {
+ if (uint32_t(pointerIndex) == entry.getPointerCount()) {
LOG_ALWAYS_FATAL("Should find a valid pointer index by id %d", mDragState->pointerId);
}
@@ -2886,7 +2843,6 @@
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const {
std::vector<InputTarget>::iterator it =
@@ -2908,8 +2864,54 @@
it = inputTargets.end() - 1;
}
- ALOG_ASSERT(it->flags == targetFlags);
- ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+ LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
+ LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
+}
+
+void InputDispatcher::addPointerWindowTargetLocked(
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const
+ REQUIRES(mLock) {
+ if (pointerIds.none()) {
+ for (const auto& target : inputTargets) {
+ LOG(INFO) << "Target: " << target;
+ }
+ LOG(FATAL) << "No pointers specified for " << windowHandle->getName();
+ return;
+ }
+ std::vector<InputTarget>::iterator it =
+ std::find_if(inputTargets.begin(), inputTargets.end(),
+ [&windowHandle](const InputTarget& inputTarget) {
+ return inputTarget.inputChannel->getConnectionToken() ==
+ windowHandle->getToken();
+ });
+
+ // This is a hack, because the actual entry could potentially be an ACTION_DOWN event that
+ // causes a HOVER_EXIT to be generated. That means that the same entry of ACTION_DOWN would
+ // have DISPATCH_AS_HOVER_EXIT and DISPATCH_AS_IS. And therefore, we have to create separate
+ // input targets for hovering pointers and for touching pointers.
+ // If we picked an existing input target above, but it's for HOVER_EXIT - let's use a new
+ // target instead.
+ if (it != inputTargets.end() && it->flags.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) {
+ // Force the code below to create a new input target
+ it = inputTargets.end();
+ }
+
+ const WindowInfo* windowInfo = windowHandle->getInfo();
+
+ if (it == inputTargets.end()) {
+ std::optional<InputTarget> target =
+ createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
+ if (!target) {
+ return;
+ }
+ inputTargets.push_back(*target);
+ it = inputTargets.end() - 1;
+ }
+
+ LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
+ LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
it->addPointers(pointerIds, windowInfo->transform);
}
@@ -3225,7 +3227,7 @@
ftl::enum_string(eventEntry->type).c_str());
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
+ if (inputTarget.pointerIds.count() != originalMotionEntry.getPointerCount()) {
if (!inputTarget.firstDownTimeInTarget.has_value()) {
logDispatchStateLocked();
LOG(FATAL) << "Splitting motion events requires a down time to be set for the "
@@ -3367,6 +3369,27 @@
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
}
+ // Check if we need to cancel any of the ongoing gestures. We don't support multiple
+ // devices being active at the same time in the same window, so if a new device is
+ // active, cancel the gesture from the old device.
+
+ std::unique_ptr<EventEntry> cancelEvent =
+ connection->inputState
+ .cancelConflictingInputStream(motionEntry,
+ dispatchEntry->resolvedAction);
+ if (cancelEvent != nullptr) {
+ LOG(INFO) << "Canceling pointers for device " << motionEntry.deviceId << " in "
+ << connection->getInputChannelName() << " with event "
+ << cancelEvent->getDescription();
+ std::unique_ptr<DispatchEntry> cancelDispatchEntry =
+ createDispatchEntry(inputTarget, std::move(cancelEvent),
+ InputTarget::Flags::DISPATCH_AS_IS);
+
+ // Send these cancel events to the queue before sending the event from the new
+ // device.
+ connection->outboundQueue.emplace_back(std::move(cancelDispatchEntry));
+ }
+
if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
LOG(WARNING) << "channel " << connection->getInputChannelName()
@@ -3537,14 +3560,14 @@
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
PointerCoords scaledCoords[MAX_POINTERS];
- const PointerCoords* usingCoords = motionEntry.pointerCoords;
+ const PointerCoords* usingCoords = motionEntry.pointerCoords.data();
// Set the X and Y offset and X and Y scale depending on the input source.
if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS))) {
float globalScaleFactor = dispatchEntry.globalScaleFactor;
if (globalScaleFactor != 1.0f) {
- for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ for (uint32_t i = 0; i < motionEntry.getPointerCount(); i++) {
scaledCoords[i] = motionEntry.pointerCoords[i];
// Don't apply window scale here since we don't want scale to affect raw
// coordinates. The scale will be sent back to the client and applied
@@ -3555,7 +3578,7 @@
}
} else if (dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS)) {
// We don't want the dispatch target to know the coordinates
- for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ for (uint32_t i = 0; i < motionEntry.getPointerCount(); i++) {
scaledCoords[i].clear();
}
usingCoords = scaledCoords;
@@ -3575,7 +3598,7 @@
motionEntry.yPrecision, motionEntry.xCursorPosition,
motionEntry.yCursorPosition, dispatchEntry.rawTransform,
motionEntry.downTime, motionEntry.eventTime,
- motionEntry.pointerCount, motionEntry.pointerProperties,
+ motionEntry.getPointerCount(), motionEntry.pointerProperties.data(),
usingCoords);
}
@@ -3958,29 +3981,25 @@
android_log_event_list(LOGTAG_INPUT_CANCEL)
<< connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
- sp<WindowInfoHandle> windowHandle;
- if (options.displayId) {
- windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken(),
- options.displayId.value());
- } else {
- windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken());
- }
-
const bool wasEmpty = connection->outboundQueue.empty();
+ // The target to use if we don't find a window associated with the channel.
+ const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
+ .flags = InputTarget::Flags::DISPATCH_AS_IS};
+ const auto& token = connection->inputChannel->getConnectionToken();
for (size_t i = 0; i < cancelationEvents.size(); i++) {
std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
std::vector<InputTarget> targets{};
- // The target to use if we don't find a window associated with the channel.
- const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
- .flags = InputTarget::Flags::DISPATCH_AS_IS};
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
- if (windowHandle != nullptr) {
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
- /*pointerIds=*/{}, keyEntry.downTime, targets);
+ const std::optional<int32_t> targetDisplay = keyEntry.displayId != ADISPLAY_ID_NONE
+ ? std::make_optional(keyEntry.displayId)
+ : std::nullopt;
+ if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
+ addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS,
+ keyEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
}
@@ -3989,14 +4008,18 @@
}
case EventEntry::Type::MOTION: {
const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
- if (windowHandle != nullptr) {
+ const std::optional<int32_t> targetDisplay =
+ motionEntry.displayId != ADISPLAY_ID_NONE
+ ? std::make_optional(motionEntry.displayId)
+ : std::nullopt;
+ if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
- for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount();
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
- pointerIds, motionEntry.downTime, targets);
+ addPointerWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS,
+ pointerIds, motionEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
const auto it = mDisplayInfos.find(motionEntry.displayId);
@@ -4066,12 +4089,12 @@
const auto& motionEntry = static_cast<const MotionEntry&>(*downEventEntry);
if (windowHandle != nullptr) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
- for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount();
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addWindowTargetLocked(windowHandle, targetFlags, pointerIds,
- motionEntry.downTime, targets);
+ addPointerWindowTargetLocked(windowHandle, targetFlags, pointerIds,
+ motionEntry.downTime, targets);
} else {
targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel,
.flags = targetFlags});
@@ -4127,10 +4150,10 @@
ALOG_ASSERT(pointerIds.any());
uint32_t splitPointerIndexMap[MAX_POINTERS];
- PointerProperties splitPointerProperties[MAX_POINTERS];
- PointerCoords splitPointerCoords[MAX_POINTERS];
+ std::vector<PointerProperties> splitPointerProperties;
+ std::vector<PointerCoords> splitPointerCoords;
- uint32_t originalPointerCount = originalMotionEntry.pointerCount;
+ uint32_t originalPointerCount = originalMotionEntry.getPointerCount();
uint32_t splitPointerCount = 0;
for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
@@ -4140,9 +4163,8 @@
uint32_t pointerId = uint32_t(pointerProperties.id);
if (pointerIds.test(pointerId)) {
splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
- splitPointerProperties[splitPointerCount] = pointerProperties;
- splitPointerCoords[splitPointerCount] =
- originalMotionEntry.pointerCoords[originalPointerIndex];
+ splitPointerProperties.push_back(pointerProperties);
+ splitPointerCoords.push_back(originalMotionEntry.pointerCoords[originalPointerIndex]);
splitPointerCount += 1;
}
}
@@ -4217,8 +4239,7 @@
originalMotionEntry.yPrecision,
originalMotionEntry.xCursorPosition,
originalMotionEntry.yCursorPosition, splitDownTime,
- splitPointerCount, splitPointerProperties,
- splitPointerCoords);
+ splitPointerProperties, splitPointerCoords);
if (originalMotionEntry.injectionState) {
splitMotionEntry->injectionState = originalMotionEntry.injectionState;
@@ -4437,9 +4458,8 @@
args.buttonState, args.classification, args.edgeFlags,
args.xPrecision, args.yPrecision,
args.xCursorPosition, args.yCursorPosition,
- args.downTime, args.getPointerCount(),
- args.pointerProperties.data(),
- args.pointerCoords.data());
+ args.downTime, args.pointerProperties,
+ args.pointerCoords);
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4653,6 +4673,11 @@
mLock.lock();
const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
+ const size_t pointerCount = motionEvent.getPointerCount();
+ const std::vector<PointerProperties>
+ pointerProperties(motionEvent.getPointerProperties(),
+ motionEvent.getPointerProperties() + pointerCount);
+
const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
std::unique_ptr<MotionEntry> injectedEntry =
std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
@@ -4667,33 +4692,28 @@
motionEvent.getYPrecision(),
motionEvent.getRawXCursorPosition(),
motionEvent.getRawYCursorPosition(),
- motionEvent.getDownTime(),
- motionEvent.getPointerCount(),
- motionEvent.getPointerProperties(),
- samplePointerCoords);
+ motionEvent.getDownTime(), pointerProperties,
+ std::vector<PointerCoords>(samplePointerCoords,
+ samplePointerCoords +
+ pointerCount));
transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
samplePointerCoords += motionEvent.getPointerCount();
- std::unique_ptr<MotionEntry> nextInjectedEntry =
- std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
- resolvedDeviceId, motionEvent.getSource(),
- displayId, policyFlags,
- motionEvent.getAction(),
- motionEvent.getActionButton(), flags,
- motionEvent.getMetaState(),
- motionEvent.getButtonState(),
- motionEvent.getClassification(),
- motionEvent.getEdgeFlags(),
- motionEvent.getXPrecision(),
- motionEvent.getYPrecision(),
- motionEvent.getRawXCursorPosition(),
- motionEvent.getRawYCursorPosition(),
- motionEvent.getDownTime(),
- motionEvent.getPointerCount(),
- motionEvent.getPointerProperties(),
- samplePointerCoords);
+ std::unique_ptr<MotionEntry> nextInjectedEntry = std::make_unique<
+ MotionEntry>(motionEvent.getId(), *sampleEventTimes, resolvedDeviceId,
+ motionEvent.getSource(), displayId, policyFlags,
+ motionEvent.getAction(), motionEvent.getActionButton(), flags,
+ motionEvent.getMetaState(), motionEvent.getButtonState(),
+ motionEvent.getClassification(), motionEvent.getEdgeFlags(),
+ motionEvent.getXPrecision(), motionEvent.getYPrecision(),
+ motionEvent.getRawXCursorPosition(),
+ motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(),
+ pointerProperties,
+ std::vector<PointerCoords>(samplePointerCoords,
+ samplePointerCoords +
+ pointerCount));
transformMotionEntryForInjectionLocked(*nextInjectedEntry,
motionEvent.getTransform());
injectedEntries.push(std::move(nextInjectedEntry));
@@ -4872,7 +4892,7 @@
entry.xCursorPosition = cursor.x;
entry.yCursorPosition = cursor.y;
}
- for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
entry.pointerCoords[i] =
MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
entry.pointerCoords[i]);
@@ -4905,29 +4925,26 @@
}
sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken) const {
+ const sp<IBinder>& windowHandleToken, std::optional<int32_t> displayId) const {
if (windowHandleToken == nullptr) {
return nullptr;
}
- for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
- for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
- if (windowHandle->getToken() == windowHandleToken) {
- return windowHandle;
+ if (!displayId) {
+ // Look through all displays.
+ for (auto& it : mWindowHandlesByDisplay) {
+ const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
+ for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
+ if (windowHandle->getToken() == windowHandleToken) {
+ return windowHandle;
+ }
}
}
- }
- return nullptr;
-}
-
-sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
- int displayId) const {
- if (windowHandleToken == nullptr) {
return nullptr;
}
- for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+ // Only look through the requested display.
+ for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(*displayId)) {
if (windowHandle->getToken() == windowHandleToken) {
return windowHandle;
}
@@ -5146,10 +5163,8 @@
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Touched window was removed: %s in display %" PRId32,
- touchedWindow.windowHandle->getName().c_str(), displayId);
- }
+ LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
+ << " in display %" << displayId;
std::shared_ptr<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
@@ -5980,9 +5995,8 @@
return BAD_VALUE;
}
std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds();
- if (deviceIds.size() != 1) {
- LOG(WARNING) << "Can't pilfer. Currently touching devices: " << dumpSet(deviceIds)
- << " in window: " << windowPtr->dump();
+ if (deviceIds.empty()) {
+ LOG(WARNING) << "Can't pilfer: no touching devices in window: " << windowPtr->dump();
return BAD_VALUE;
}
@@ -6825,10 +6839,11 @@
if (oldWallpaper != nullptr) {
const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
- addWindowTargetLocked(oldWallpaper,
- oldTouchedWindow.targetFlags |
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
+ addPointerWindowTargetLocked(oldWallpaper,
+ oldTouchedWindow.targetFlags |
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
+ pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId),
+ targets);
state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper);
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ee5a797..a1127a0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -354,14 +354,11 @@
// Get a reference to window handles by display, return an empty vector if not found.
const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked(
int32_t displayId) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
- // Same function as above, but faster. Since displayId is provided, this avoids the need
- // to loop through all displays.
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
- int displayId) const REQUIRES(mLock);
+ sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
+ const sp<IBinder>& windowHandleToken, std::optional<int32_t> displayId = {}) const
+ REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
@@ -527,7 +524,7 @@
nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<InputTarget> findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+ nsecs_t currentTime, const MotionEntry& entry,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<Monitor> selectResponsiveMonitorsLocked(
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
@@ -538,9 +535,13 @@
std::optional<nsecs_t> firstDownTimeInTarget) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
+ void addPointerWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget,
+ std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index b21427d..09b5186 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -24,6 +24,19 @@
namespace android::inputdispatcher {
+namespace {
+bool isHoverAction(int32_t action) {
+ switch (MotionEvent::getActionMasked(action)) {
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+ return true;
+ }
+ }
+ return false;
+}
+} // namespace
+
InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
InputState::~InputState() {}
@@ -89,6 +102,28 @@
* false if the incoming event should be dropped.
*/
bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
+ // Don't track non-pointer events
+ if (!isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ // This is a focus-dispatched event; we don't track its state.
+ return true;
+ }
+
+ if (!mMotionMementos.empty()) {
+ const MotionMemento& lastMemento = mMotionMementos.back();
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) &&
+ !isStylusEvent(entry.source, entry.pointerProperties)) {
+ // We already have a stylus stream, and the new event is not from stylus.
+ if (!lastMemento.hovering) {
+ // If stylus is currently down, reject the new event unconditionally.
+ return false;
+ }
+ }
+ if (!lastMemento.hovering && isHoverAction(action)) {
+ // Reject hovers if already down
+ return false;
+ }
+ }
+
int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
switch (actionMasked) {
case AMOTION_EVENT_ACTION_UP:
@@ -235,8 +270,10 @@
}
void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
- pointerCount = 0;
- for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ pointerProperties.clear();
+ pointerCoords.clear();
+
+ for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
if (MotionEvent::getActionMasked(entry.action) == AMOTION_EVENT_ACTION_POINTER_UP) {
// In POINTER_UP events, the pointer is leaving. Since the action is not stored,
// this departing pointer should not be recorded.
@@ -245,23 +282,155 @@
continue;
}
}
- pointerProperties[pointerCount] = entry.pointerProperties[i];
- pointerCoords[pointerCount] = entry.pointerCoords[i];
- pointerCount++;
+ pointerProperties.push_back(entry.pointerProperties[i]);
+ pointerCoords.push_back(entry.pointerCoords[i]);
}
}
void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const {
- for (uint32_t i = 0; i < pointerCount; i++) {
+ for (uint32_t i = 0; i < getPointerCount(); i++) {
if (other.firstNewPointerIdx < 0) {
- other.firstNewPointerIdx = other.pointerCount;
+ other.firstNewPointerIdx = other.getPointerCount();
}
- other.pointerProperties[other.pointerCount] = pointerProperties[i];
- other.pointerCoords[other.pointerCount] = pointerCoords[i];
- other.pointerCount++;
+ other.pointerProperties.push_back(pointerProperties[i]);
+ other.pointerCoords.push_back(pointerCoords[i]);
}
}
+size_t InputState::MotionMemento::getPointerCount() const {
+ return pointerProperties.size();
+}
+
+bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry,
+ int32_t resolvedAction) const {
+ if (!isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ // This is a focus-dispatched event that should not affect the previous stream.
+ return false;
+ }
+
+ // New MotionEntry pointer event is coming in.
+
+ // If this is a new gesture, and it's from a different device, then, in general, we will cancel
+ // the current gesture.
+ // However, because stylus should be preferred over touch, we need to treat some cases in a
+ // special way.
+ if (mMotionMementos.empty()) {
+ // There is no ongoing pointer gesture, so there is nothing to cancel
+ return false;
+ }
+
+ const MotionMemento& lastMemento = mMotionMementos.back();
+ const int32_t actionMasked = MotionEvent::getActionMasked(resolvedAction);
+
+ // For compatibility, only one input device can be active at a time in the same window.
+ if (lastMemento.deviceId == motionEntry.deviceId) {
+ // In general, the same device should produce self-consistent streams so nothing needs to
+ // be canceled. But there is one exception:
+ // Sometimes ACTION_DOWN is received without a corresponding HOVER_EXIT. To account for
+ // that, cancel the previous hovering stream
+ if (actionMasked == AMOTION_EVENT_ACTION_DOWN && lastMemento.hovering) {
+ return true;
+ }
+
+ // Use the previous stream cancellation logic to generate all HOVER_EXIT events.
+ // If this hover event was generated as a result of the pointer leaving the window,
+ // the HOVER_EXIT event should have the same coordinates as the previous
+ // HOVER_MOVE event in this stream. Ensure that all HOVER_EXITs have the same
+ // coordinates as the previous event by cancelling the stream here. With this approach, the
+ // HOVER_EXIT event is generated from the previous event.
+ if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT && lastMemento.hovering) {
+ return true;
+ }
+
+ // If the stream changes its source, just cancel the current gesture to be safe. It's
+ // possible that the app isn't handling source changes properly
+ if (motionEntry.source != lastMemento.source) {
+ LOG(INFO) << "Canceling stream: last source was "
+ << inputEventSourceToString(lastMemento.source) << " and new event is "
+ << motionEntry;
+ return true;
+ }
+
+ // If the injection is happening into two different displays, the same injected device id
+ // could be going into both. And at this time, if mirroring is active, the same connection
+ // would receive different events from each display. Since the TouchStates are per-display,
+ // it's unlikely that those two streams would be consistent with each other. Therefore,
+ // cancel the previous gesture if the display id changes.
+ if (motionEntry.displayId != lastMemento.displayId) {
+ LOG(INFO) << "Canceling stream: last displayId was "
+ << inputEventSourceToString(lastMemento.displayId) << " and new event is "
+ << motionEntry;
+ return true;
+ }
+
+ return false;
+ }
+
+ // We want stylus down to block touch and other source types, but stylus hover should not
+ // have such an effect.
+ if (isHoverAction(motionEntry.action) && !lastMemento.hovering) {
+ // New event is a hover. Keep the current non-hovering gesture instead
+ return false;
+ }
+
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) && !lastMemento.hovering) {
+ // We have non-hovering stylus already active.
+ if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) &&
+ actionMasked == AMOTION_EVENT_ACTION_DOWN) {
+ // If this new event is a stylus from a different device going down, then cancel the old
+ // stylus and allow the new stylus to take over
+ return true;
+ }
+
+ // Keep the current stylus gesture.
+ return false;
+ }
+
+ // Cancel the current gesture if this is a start of a new gesture from a new device.
+ if (actionMasked == AMOTION_EVENT_ACTION_DOWN ||
+ actionMasked == AMOTION_EVENT_ACTION_HOVER_ENTER) {
+ return true;
+ }
+ // By default, don't cancel any events.
+ return false;
+}
+
+std::unique_ptr<EventEntry> InputState::cancelConflictingInputStream(const MotionEntry& motionEntry,
+ int32_t resolvedAction) {
+ if (!shouldCancelPreviousStream(motionEntry, resolvedAction)) {
+ return {};
+ }
+
+ const MotionMemento& memento = mMotionMementos.back();
+
+ // Cancel the last device stream
+ std::unique_ptr<MotionEntry> cancelEntry =
+ createCancelEntryForMemento(memento, motionEntry.eventTime);
+
+ if (!trackMotion(*cancelEntry, cancelEntry->action, cancelEntry->flags)) {
+ LOG(FATAL) << "Generated inconsistent cancel event!";
+ }
+ return cancelEntry;
+}
+
+std::unique_ptr<MotionEntry> InputState::createCancelEntryForMemento(const MotionMemento& memento,
+ nsecs_t eventTime) const {
+ const int32_t action =
+ memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
+ int32_t flags = memento.flags;
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ return std::make_unique<MotionEntry>(mIdGenerator.nextId(), eventTime, memento.deviceId,
+ memento.source, memento.displayId, memento.policyFlags,
+ action, /*actionButton=*/0, flags, AMETA_NONE,
+ /*buttonState=*/0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ memento.pointerProperties, memento.pointerCoords);
+}
+
std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options) {
std::vector<std::unique_ptr<EventEntry>> events;
@@ -280,25 +449,7 @@
for (const MotionMemento& memento : mMotionMementos) {
if (shouldCancelMotion(memento, options)) {
if (options.pointerIds == std::nullopt) {
- const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
- : AMOTION_EVENT_ACTION_CANCEL;
- int32_t flags = memento.flags;
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- flags |= AMOTION_EVENT_FLAG_CANCELED;
- }
- events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
- memento.deviceId, memento.source,
- memento.displayId, memento.policyFlags,
- action, /*actionButton=*/0, flags, AMETA_NONE,
- /*buttonState=*/0, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- memento.xPrecision, memento.yPrecision,
- memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- memento.pointerCount,
- memento.pointerProperties,
- memento.pointerCoords));
+ events.push_back(createCancelEntryForMemento(memento, currentTime));
} else {
std::vector<std::unique_ptr<MotionEntry>> pointerCancelEvents =
synthesizeCancelationEventsForPointers(memento, options.pointerIds.value(),
@@ -323,23 +474,22 @@
continue;
}
- uint32_t pointerCount = 0;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
// We will deliver all pointers the target already knows about
for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
- pointerProperties[i] = memento.pointerProperties[i];
- pointerCoords[i] = memento.pointerCoords[i];
- pointerCount++;
+ pointerProperties.push_back(memento.pointerProperties[i]);
+ pointerCoords.push_back(memento.pointerCoords[i]);
}
// We will send explicit events for all pointers the target doesn't know about
for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
- i < memento.pointerCount; i++) {
- pointerProperties[i] = memento.pointerProperties[i];
- pointerCoords[i] = memento.pointerCoords[i];
- pointerCount++;
+ i < memento.getPointerCount(); i++) {
+ pointerProperties.push_back(memento.pointerProperties[i]);
+ pointerCoords.push_back(memento.pointerCoords[i]);
+
+ const size_t pointerCount = pointerProperties.size();
// Down only if the first pointer, pointer down otherwise
const int32_t action = (pointerCount <= 1)
@@ -356,7 +506,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
- pointerCount, pointerProperties, pointerCoords));
+ pointerProperties, pointerCoords));
}
memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
@@ -372,7 +522,7 @@
std::vector<uint32_t> canceledPointerIndices;
std::vector<PointerProperties> pointerProperties(MAX_POINTERS);
std::vector<PointerCoords> pointerCoords(MAX_POINTERS);
- for (uint32_t pointerIdx = 0; pointerIdx < memento.pointerCount; pointerIdx++) {
+ for (uint32_t pointerIdx = 0; pointerIdx < memento.getPointerCount(); pointerIdx++) {
uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
pointerProperties[pointerIdx] = memento.pointerProperties[pointerIdx];
pointerCoords[pointerIdx] = memento.pointerCoords[pointerIdx];
@@ -381,7 +531,7 @@
}
}
- if (canceledPointerIndices.size() == memento.pointerCount) {
+ if (canceledPointerIndices.size() == memento.getPointerCount()) {
const int32_t action =
memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
int32_t flags = memento.flags;
@@ -397,8 +547,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
- memento.pointerCount, memento.pointerProperties,
- memento.pointerCoords));
+ memento.pointerProperties, memento.pointerCoords));
} else {
// If we aren't canceling all pointers, we need to generate ACTION_POINTER_UP with
// FLAG_CANCELED for each of the canceled pointers. For each event, we must remove the
@@ -409,7 +558,7 @@
std::sort(canceledPointerIndices.begin(), canceledPointerIndices.end(),
std::greater<uint32_t>());
- uint32_t pointerCount = memento.pointerCount;
+ uint32_t pointerCount = memento.getPointerCount();
for (const uint32_t pointerIdx : canceledPointerIndices) {
const int32_t action = pointerCount == 1 ? AMOTION_EVENT_ACTION_CANCEL
: AMOTION_EVENT_ACTION_POINTER_UP |
@@ -425,8 +574,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
- pointerCount, pointerProperties.data(),
- pointerCoords.data()));
+ pointerProperties, pointerCoords));
// Cleanup pointer information
pointerProperties.erase(pointerProperties.begin() + pointerIdx);
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 32df034..686c432 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -48,6 +48,10 @@
// and should be skipped.
bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
+ // Create cancel events for the previous stream if the current motionEntry requires it.
+ std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry,
+ int32_t resolvedAction);
+
// Synthesizes cancelation events for the current state and resets the tracked state.
std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options);
@@ -97,9 +101,8 @@
float xCursorPosition;
float yCursorPosition;
nsecs_t downTime;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
// Track for which pointers the target doesn't know about.
int32_t firstNewPointerIdx = INVALID_POINTER_INDEX;
bool hovering;
@@ -107,6 +110,7 @@
void setPointers(const MotionEntry& entry);
void mergePointerStateTo(MotionMemento& other) const;
+ size_t getPointerCount() const;
};
const IdGenerator& mIdGenerator; // InputDispatcher owns it so we won't have dangling reference.
@@ -123,6 +127,9 @@
static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
+ bool shouldCancelPreviousStream(const MotionEntry& motionEntry, int32_t resolvedAction) const;
+ std::unique_ptr<MotionEntry> createCancelEntryForMemento(const MotionMemento& memento,
+ nsecs_t eventTime) const;
// Synthesizes pointer cancel events for a particular set of pointers.
std::vector<std::unique_ptr<MotionEntry>> synthesizeCancelationEventsForPointers(
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 9dcf615..2ead171 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <gui/WindowInfo.h>
@@ -31,15 +32,6 @@
*this = TouchState();
}
-std::set<int32_t> TouchState::getActiveDeviceIds() const {
- std::set<int32_t> out;
- for (const TouchedWindow& w : windows) {
- std::set<int32_t> deviceIds = w.getActiveDeviceIds();
- out.insert(deviceIds.begin(), deviceIds.end());
- }
- return out;
-}
-
bool TouchState::hasTouchingPointers(DeviceId deviceId) const {
return std::any_of(windows.begin(), windows.end(), [&](const TouchedWindow& window) {
return window.hasTouchingPointers(deviceId);
@@ -65,9 +57,9 @@
}
}
-void TouchState::clearHoveringPointers() {
+void TouchState::clearHoveringPointers(DeviceId deviceId) {
for (TouchedWindow& touchedWindow : windows) {
- touchedWindow.clearHoveringPointers();
+ touchedWindow.removeAllHoveringPointersForDevice(deviceId);
}
clearWindowsWithoutPointers();
}
@@ -82,9 +74,16 @@
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget) {
+ if (touchingPointerIds.none()) {
+ LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
+ return;
+ }
for (TouchedWindow& touchedWindow : windows) {
// We do not compare windows by token here because two windows that share the same token
- // may have a different transform
+ // may have a different transform. They will be combined later when we create InputTargets.
+ // At that point, per-pointer window transform will be considered.
+ // An alternative design choice here would have been to compare here by token, but to
+ // store per-pointer transform.
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
@@ -237,14 +236,16 @@
return *it;
}
-bool TouchState::isDown() const {
- return std::any_of(windows.begin(), windows.end(),
- [](const TouchedWindow& window) { return window.hasTouchingPointers(); });
+bool TouchState::isDown(DeviceId deviceId) const {
+ return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
+ return window.hasTouchingPointers(deviceId);
+ });
}
-bool TouchState::hasHoveringPointers() const {
- return std::any_of(windows.begin(), windows.end(),
- [](const TouchedWindow& window) { return window.hasHoveringPointers(); });
+bool TouchState::hasHoveringPointers(DeviceId deviceId) const {
+ return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
+ return window.hasHoveringPointers(deviceId);
+ });
}
std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index f016936..e79c73b 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -39,8 +39,6 @@
void reset();
void clearWindowsWithoutPointers();
- std::set<DeviceId> getActiveDeviceIds() const;
-
bool hasTouchingPointers(DeviceId deviceId) const;
void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
void removeTouchingPointerFromWindow(DeviceId deviceId, int32_t pointerId,
@@ -52,7 +50,7 @@
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, int32_t hoveringPointerId);
void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId);
- void clearHoveringPointers();
+ void clearHoveringPointers(DeviceId deviceId);
void removeAllPointersForDevice(DeviceId deviceId);
void removeWindowByToken(const sp<IBinder>& token);
@@ -72,8 +70,8 @@
const TouchedWindow& getTouchedWindow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const;
// Whether any of the windows are currently being touched
- bool isDown() const;
- bool hasHoveringPointers() const;
+ bool isDown(DeviceId deviceId) const;
+ bool hasHoveringPointers(DeviceId deviceId) const;
std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
DeviceId deviceId, int32_t pointerId) const;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index ff4b425..5367751 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -45,12 +45,16 @@
return state.hoveringPointerIds.any();
}
-void TouchedWindow::clearHoveringPointers() {
- for (auto& [_, state] : mDeviceStates) {
- state.hoveringPointerIds.reset();
+void TouchedWindow::clearHoveringPointers(DeviceId deviceId) {
+ auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
}
-
- std::erase_if(mDeviceStates, [](const auto& pair) { return !pair.second.hasPointers(); });
+ DeviceState& state = stateIt->second;
+ state.hoveringPointerIds.reset();
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
+ }
}
bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const {
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 3f760c0..6d2283e 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -71,7 +71,7 @@
void removeAllTouchingPointersForDevice(DeviceId deviceId);
void removeAllHoveringPointersForDevice(DeviceId deviceId);
- void clearHoveringPointers();
+ void clearHoveringPointers(DeviceId deviceId);
std::string dump() const;
private:
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 90bd7c9..3c26d1d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3782,34 +3782,26 @@
return out;
}
-static bool isStylusEvent(uint32_t source, int32_t action, const PointerProperties* properties) {
- if (!isFromSource(source, AINPUT_SOURCE_STYLUS)) {
- return false;
- }
- const auto actionIndex = action >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- return isStylusToolType(properties[actionIndex].toolType);
-}
-
NotifyMotionArgs TouchInputMapper::dispatchMotion(
nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
float yPrecision, nsecs_t downTime, MotionClassification classification) {
- PointerCoords pointerCoords[MAX_POINTERS];
- PointerProperties pointerProperties[MAX_POINTERS];
+ std::vector<PointerCoords> pointerCoords;
+ std::vector<PointerProperties> pointerProperties;
uint32_t pointerCount = 0;
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = idToIndex[id];
- pointerProperties[pointerCount] = properties[index];
- pointerCoords[pointerCount] = coords[index];
+ pointerProperties.push_back(properties[index]);
+ pointerCoords.push_back(coords[index]);
if (changedId >= 0 && id == uint32_t(changedId)) {
action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
- pointerCount += 1;
+ pointerCount++;
}
ALOG_ASSERT(pointerCount != 0);
@@ -3837,7 +3829,7 @@
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const bool showDirectStylusPointer = mConfig.stylusPointerIconEnabled &&
- mDeviceMode == DeviceMode::DIRECT && isStylusEvent(source, action, pointerProperties) &&
+ mDeviceMode == DeviceMode::DIRECT && isStylusEvent(source, pointerProperties) &&
mPointerController && displayId != ADISPLAY_ID_NONE &&
displayId == mPointerController->getDisplayId();
if (showDirectStylusPointer) {
@@ -3869,9 +3861,9 @@
[this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
return NotifyMotionArgs(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
policyFlags, action, actionButton, flags, metaState, buttonState,
- classification, edgeFlags, pointerCount, pointerProperties,
- pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
- downTime, std::move(frames));
+ classification, edgeFlags, pointerCount, pointerProperties.data(),
+ pointerCoords.data(), xPrecision, yPrecision, xCursorPosition,
+ yCursorPosition, downTime, std::move(frames));
}
std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2f63b2a..3c87f71 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2409,9 +2409,10 @@
using InputDispatcherMultiDeviceTest = InputDispatcherTest;
/**
- * One window. Stylus down on the window. Next, touch from another device goes down.
+ * One window. Stylus down on the window. Next, touch from another device goes down. Ensure that
+ * touch is dropped, because stylus should be preferred over touch.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusDownAndTouchDown) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownBlocksTouchDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2434,34 +2435,31 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
.build());
- // Touch cancels stylus
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId),
- WithCoords(100, 110)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
- WithCoords(140, 145)));
// Touch move
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
.build());
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
- WithCoords(141, 146)));
+ // Touch is ignored because stylus is already down
- // Subsequent stylus movements are dropped
+ // Subsequent stylus movements are delivered correctly
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+
window->assertNoEvents();
}
/**
* One window and one spy window. Stylus down on the window. Next, touch from another device goes
- * down.
+ * down. Ensure that touch is dropped, because stylus should be preferred over touch.
* Similar test as above, but with added SPY window.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyAndTouchDown) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyBlocksTouchDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2497,30 +2495,28 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
.build());
- window->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
- // Subsequent stylus movements are dropped
+
+ // Touch is ignored because stylus is already down
+
+ // Subsequent stylus movements are delivered correctly
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
window->assertNoEvents();
spyWindow->assertNoEvents();
}
/**
- * One window. Stylus hover on the window. Next, touch from another device goes down.
+ * One window. Stylus hover on the window. Next, touch from another device goes down. Ensure that
+ * touch is not dropped, because stylus hover should be ignored.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchDown) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2548,19 +2544,174 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
.build());
- window->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus hover is canceled because touch is down
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT),
+ WithDeviceId(stylusDeviceId), WithCoords(100, 110)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
+ WithCoords(140, 145)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(141, 146)));
+
// Subsequent stylus movements are ignored
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
+
+ // but subsequent touches continue to be delivered
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(142, 147)));
+}
+
+/**
+ * One window. Touch down on the window. Then, stylus hover on the window from another device.
+ * Ensure that touch is not canceled, because stylus hover should be dropped.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus hover on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ // Stylus hover movement is dropped
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
+ .build());
+ // Subsequent touch movements are delivered correctly
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(142, 147)));
+}
+
+/**
+ * One window. Stylus down on the window. Then, stylus from another device goes down. Ensure that
+ * the latest stylus takes over. That is, old stylus should be canceled and the new stylus should
+ * become active.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, LatestStylusWins) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t stylusDeviceId1 = 3;
+ constexpr int32_t stylusDeviceId2 = 5;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(99).y(100))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(101))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId1)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId1)));
+
+ // Second stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId2)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(9).y(10))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId2)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(10).y(11))
+ .build());
+
+ // First stylus is canceled, second one takes over.
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId1)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId2)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId2)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102))
+ .build());
+ // Subsequent stylus movements are delivered correctly
window->assertNoEvents();
}
/**
+ * One window. Touch down on the window. Then, stylus down on the window from another device.
+ * Ensure that is canceled, because stylus down should be preferred over touch.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, TouchIsCanceledByStylusDown) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Subsequent stylus movements are delivered correctly
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
* down. Then, on the left window, also place second touch pointer down.
@@ -2617,8 +2768,8 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(mouseDeviceId)));
+ leftWindow->assertNoEvents();
+
rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Second touch pointer down on left window
@@ -2627,6 +2778,11 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
.build());
+ // Since this is now a new splittable pointer going down on the left window, and it's coming
+ // from a different device, the current gesture in the left window (pointer down) should first
+ // be canceled.
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(mouseDeviceId)));
leftWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
// This MOVE event is not necessary (doesn't carry any new information), but it's there in the
@@ -2672,8 +2828,6 @@
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
@@ -2683,18 +2837,14 @@
.pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
.build());
leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
- rightWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(mouseDeviceId)));
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(310).y(110))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
rightWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
leftWindow->assertNoEvents();
rightWindow->assertNoEvents();
@@ -2745,29 +2895,30 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ leftWindow->assertNoEvents();
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Stylus movements continue, but are ignored because the touch went down more recently.
+ // Spy window does not receive touch events, because stylus events take precedence, and it
+ // already has an active stylus gesture.
+
+ // Stylus movements continue. They should be delivered to the left window and to the spy window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
.build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ // Further MOVE events keep going to the right window only
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(310).y(110))
.build());
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
spyWindow->assertNoEvents();
leftWindow->assertNoEvents();
@@ -2779,8 +2930,10 @@
* both.
* Check hover in left window and touch down in the right window.
* At first, spy should receive hover, but the touch down should cancel hovering inside spy.
+ * At the same time, left and right should be getting independent streams of hovering and touch,
+ * respectively.
*/
-TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverAndTouchWithSpy) {
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow =
@@ -2813,13 +2966,13 @@
spyWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
- // Touch down on the right window.
+ // Touch down on the right window. Spy doesn't receive this touch because it already has
+ // stylus hovering there.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ leftWindow->assertNoEvents();
spyWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
spyWindow->consumeMotionEvent(
@@ -2827,11 +2980,13 @@
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Stylus movements continue, but are ignored because the touch is down.
+ // Stylus movements continue. They should be delivered to the left window only.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
.build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
// Touch movements continue. They should be delivered to the right window and to the spy
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
@@ -3054,7 +3209,7 @@
* While the touch is down, new hover events from the stylus device should be ignored. After the
* touch is gone, stylus hovering should start working again.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchTap) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3080,20 +3235,20 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
+ // The touch device should cause hover to stop!
window->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Continue hovering with stylus. Injection will fail because touch is already down.
- ASSERT_EQ(InputEventInjectionResult::FAILED,
+ // Continue hovering with stylus.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60))
.build()));
- // No event should be sent. This event should be ignored because a pointer from another device
- // is already down.
+ // Hovers are now ignored
// Lift up the finger
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -3105,7 +3260,6 @@
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId)));
- // Now that the touch is gone, stylus hovering should start working again
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
@@ -4682,6 +4836,38 @@
firstWindow->assertNoEvents();
}
+// Same as above, but while the window is being mirrored.
+TEST_F(InputDispatcherDisplayProjectionTest,
+ SynthesizeHoverEnterExitWithCorrectCoordinatesWhenMirrored) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set(matrix);
+ addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
+
+ sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
+ secondWindowClone->setWindowTransform(1.1, 2.2, 3.3, 4.4);
+ addWindow(secondWindowClone);
+
+ // Send hover move to the second window, and ensure it shows up as hover enter.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithCoords(100, 80), WithRawCoords(300, 880)));
+
+ // Touch down at the same location and ensure a hover exit is synthesized for the correct
+ // display.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+ WithRawCoords(300, 880)));
+ secondWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80), WithRawCoords(300, 880)));
+ secondWindow->assertNoEvents();
+ firstWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeHoverCancelationWithCorrectCoordinates) {
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
@@ -4699,6 +4885,37 @@
firstWindow->assertNoEvents();
}
+// Same as above, but while the window is being mirrored.
+TEST_F(InputDispatcherDisplayProjectionTest,
+ SynthesizeHoverCancelationWithCorrectCoordinatesWhenMirrored) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set(matrix);
+ addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
+
+ sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
+ secondWindowClone->setWindowTransform(1.1, 2.2, 3.3, 4.4);
+ addWindow(secondWindowClone);
+
+ // Send hover enter to second window
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithCoords(100, 80), WithRawCoords(300, 880),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ mDispatcher->cancelCurrentTouch();
+
+ // Ensure the cancelation happens with the correct displayId and the correct coordinates.
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+ WithRawCoords(300, 880),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ secondWindow->assertNoEvents();
+ firstWindow->assertNoEvents();
+}
+
/** Ensure consistent behavior of InputDispatcher in all orientations. */
class InputDispatcherDisplayOrientationFixture
: public InputDispatcherDisplayProjectionTest,
@@ -7759,10 +7976,23 @@
// should be ANR'd first.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- FOCUSED_WINDOW_LOCATION))
- << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER)
+ .x(FOCUSED_WINDOW_LOCATION.x)
+ .y(FOCUSED_WINDOW_LOCATION.y))
+ .build()));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER)
+ .x(FOCUSED_WINDOW_LOCATION.x)
+ .y(FOCUSED_WINDOW_LOCATION.y))
+ .build()));
mFocusedWindow->consumeMotionDown();
+ mFocusedWindow->consumeMotionUp();
mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
ADISPLAY_ID_DEFAULT, /*flags=*/0);
// We consumed all events, so no ANR
@@ -7770,17 +8000,20 @@
mFakePolicy->assertNotifyAnrWasNotCalled();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- FOCUSED_WINDOW_LOCATION));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER)
+ .x(FOCUSED_WINDOW_LOCATION.x)
+ .y(FOCUSED_WINDOW_LOCATION.y))
+ .build()));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
- // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
- // sequence to make it consistent
- mFocusedWindow->consumeMotionCancel();
+
mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
mFocusedWindow->consumeMotionDown();
// This cancel is generated because the connection was unresponsive
@@ -10293,13 +10526,12 @@
.build());
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spy->assertNoEvents();
// Act: pilfer from spy. Spy is currently receiving touch events.
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
@@ -10313,7 +10545,7 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(52))
.build());
- spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
spy->assertNoEvents();
leftWindow->assertNoEvents();
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 3155b4c..dd83fde 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -37,6 +37,8 @@
#include <cinttypes>
#include <cstddef>
#include <thread>
+#include <mutex>
+#include <condition_variable>
using namespace android::hardware::sensors;
using android::util::ProtoOutputStream;
@@ -65,7 +67,7 @@
} // anonymous namespace
-SensorDevice::SensorDevice() {
+SensorDevice::SensorDevice() : mInHalBypassMode(false) {
if (!connectHalService()) {
return;
}
@@ -352,13 +354,17 @@
if (mHalWrapper == nullptr) return NO_INIT;
ssize_t eventsRead = 0;
- if (mHalWrapper->supportsMessageQueues()) {
- eventsRead = mHalWrapper->pollFmq(buffer, count);
- } else if (mHalWrapper->supportsPolling()) {
- eventsRead = mHalWrapper->poll(buffer, count);
+ if (mInHalBypassMode) [[unlikely]] {
+ eventsRead = getHalBypassInjectedEvents(buffer, count);
} else {
- ALOGE("Must support polling or FMQ");
- eventsRead = -1;
+ if (mHalWrapper->supportsMessageQueues()) {
+ eventsRead = mHalWrapper->pollFmq(buffer, count);
+ } else if (mHalWrapper->supportsPolling()) {
+ eventsRead = mHalWrapper->poll(buffer, count);
+ } else {
+ ALOGE("Must support polling or FMQ");
+ eventsRead = -1;
+ }
}
if (eventsRead > 0) {
@@ -762,11 +768,38 @@
injected_sensor_event->data[2], injected_sensor_event->data[3],
injected_sensor_event->data[4], injected_sensor_event->data[5]);
+ if (mInHalBypassMode) {
+ std::lock_guard _l(mHalBypassLock);
+ mHalBypassInjectedEventQueue.push(*injected_sensor_event);
+ mHalBypassCV.notify_one();
+ return OK;
+ }
return mHalWrapper->injectSensorData(injected_sensor_event);
}
status_t SensorDevice::setMode(uint32_t mode) {
if (mHalWrapper == nullptr) return NO_INIT;
+ if (mode == SensorService::Mode::HAL_BYPASS_REPLAY_DATA_INJECTION) {
+ if (!mInHalBypassMode) {
+ std::lock_guard _l(mHalBypassLock);
+ while (!mHalBypassInjectedEventQueue.empty()) {
+ // flush any stale events from the injected event queue
+ mHalBypassInjectedEventQueue.pop();
+ }
+ mInHalBypassMode = true;
+ }
+ return OK;
+ } else {
+ if (mInHalBypassMode) {
+ // We are transitioning out of HAL Bypass mode. We need to notify the reader thread
+ // (specifically getHalBypassInjectedEvents()) of this change in state so that it is not
+ // stuck waiting on more injected events to come and therefore preventing events coming
+ // from the HAL from being read.
+ std::lock_guard _l(mHalBypassLock);
+ mInHalBypassMode = false;
+ mHalBypassCV.notify_one();
+ }
+ }
return mHalWrapper->setOperationMode(static_cast<SensorService::Mode>(mode));
}
@@ -872,5 +905,24 @@
return 0;
}
+ssize_t SensorDevice::getHalBypassInjectedEvents(sensors_event_t* buffer,
+ size_t maxNumEventsToRead) {
+ std::unique_lock _l(mHalBypassLock);
+ if (mHalBypassInjectedEventQueue.empty()) {
+ // if the injected event queue is empty, block and wait till there are events to process
+ // or if we are no longer in HAL Bypass mode so that this method is not called in a tight
+ // loop. Otherwise, continue copying the injected events into the supplied buffer.
+ mHalBypassCV.wait(_l, [this] {
+ return (!mHalBypassInjectedEventQueue.empty() || !mInHalBypassMode);
+ });
+ }
+ size_t eventsToRead = std::min(mHalBypassInjectedEventQueue.size(), maxNumEventsToRead);
+ for (size_t i = 0; i < eventsToRead; i++) {
+ buffer[i] = mHalBypassInjectedEventQueue.front();
+ mHalBypassInjectedEventQueue.pop();
+ }
+ return eventsToRead;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 747a6b0..f127c0f 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -35,6 +35,9 @@
#include <utils/Timers.h>
#include <algorithm> //std::max std::min
+#include <condition_variable>
+#include <mutex>
+#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
@@ -225,6 +228,12 @@
float getResolutionForSensor(int sensorHandle);
bool mIsDirectReportSupported;
+
+ std::mutex mHalBypassLock;
+ std::condition_variable mHalBypassCV;
+ std::queue<sensors_event_t> mHalBypassInjectedEventQueue;
+ ssize_t getHalBypassInjectedEvents(sensors_event_t* buffer, size_t count);
+ bool mInHalBypassMode;
};
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 9c51fd9..44d0d70 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -665,6 +665,10 @@
result.appendFormat(" REPLAY_DATA_INJECTION : %s\n",
mAllowListedPackage.c_str());
break;
+ case HAL_BYPASS_REPLAY_DATA_INJECTION:
+ result.appendFormat(" HAL_BYPASS_REPLAY_DATA_INJECTION : %s\n",
+ mAllowListedPackage.c_str());
+ break;
default:
result.appendFormat(" UNKNOWN\n");
break;
@@ -1529,10 +1533,9 @@
sp<ISensorEventConnection> SensorService::createSensorEventConnection(const String8& packageName,
int requestedMode, const String16& opPackageName, const String16& attributionTag) {
- // Only 3 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION and
- // REPLAY_DATA_INJECTION.
- if (requestedMode != NORMAL && requestedMode != DATA_INJECTION &&
- requestedMode != REPLAY_DATA_INJECTION) {
+ // Only 4 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION,
+ // REPLAY_DATA_INJECTION and HAL_BYPASS_REPLAY_DATA_INJECTION
+ if (requestedMode != NORMAL && !isInjectionMode(requestedMode)) {
return nullptr;
}
resetTargetSdkVersionCache(opPackageName);
@@ -1553,9 +1556,9 @@
String16 connOpPackageName =
(opPackageName == String16("")) ? String16(connPackageName) : opPackageName;
sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName,
- requestedMode == DATA_INJECTION || requestedMode == REPLAY_DATA_INJECTION,
- connOpPackageName, attributionTag));
- if (requestedMode == DATA_INJECTION || requestedMode == REPLAY_DATA_INJECTION) {
+ isInjectionMode(requestedMode),
+ connOpPackageName, attributionTag));
+ if (isInjectionMode(requestedMode)) {
mConnectionHolder.addEventConnectionIfNotPresent(result);
// Add the associated file descriptor to the Looper for polling whenever there is data to
// be injected.
@@ -1566,7 +1569,22 @@
int SensorService::isDataInjectionEnabled() {
Mutex::Autolock _l(mLock);
- return (mCurrentOperatingMode == DATA_INJECTION);
+ return mCurrentOperatingMode == DATA_INJECTION;
+}
+
+int SensorService::isReplayDataInjectionEnabled() {
+ Mutex::Autolock _l(mLock);
+ return mCurrentOperatingMode == REPLAY_DATA_INJECTION;
+}
+
+int SensorService::isHalBypassReplayDataInjectionEnabled() {
+ Mutex::Autolock _l(mLock);
+ return mCurrentOperatingMode == HAL_BYPASS_REPLAY_DATA_INJECTION;
+}
+
+bool SensorService::isInjectionMode(int mode) {
+ return (mode == DATA_INJECTION || mode == REPLAY_DATA_INJECTION ||
+ mode == HAL_BYPASS_REPLAY_DATA_INJECTION);
}
sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
@@ -2332,6 +2350,10 @@
*targetModeOut = REPLAY_DATA_INJECTION;
return true;
}
+ if (inputString == std::string("hal_bypass_replay_data_injection")) {
+ *targetModeOut = HAL_BYPASS_REPLAY_DATA_INJECTION;
+ return true;
+ }
return false;
}
@@ -2357,7 +2379,8 @@
dev.disableAllSensors();
}
if (mCurrentOperatingMode == DATA_INJECTION ||
- mCurrentOperatingMode == REPLAY_DATA_INJECTION) {
+ mCurrentOperatingMode == REPLAY_DATA_INJECTION ||
+ mCurrentOperatingMode == HAL_BYPASS_REPLAY_DATA_INJECTION) {
resetToNormalModeLocked();
}
mAllowListedPackage.clear();
@@ -2373,6 +2396,8 @@
disableAllSensorsLocked(&connLock);
mAllowListedPackage = String8(args[1]);
return status_t(NO_ERROR);
+ case HAL_BYPASS_REPLAY_DATA_INJECTION:
+ FALLTHROUGH_INTENDED;
case REPLAY_DATA_INJECTION:
if (SensorServiceUtil::isUserBuild()) {
return INVALID_OPERATION;
@@ -2381,9 +2406,16 @@
case DATA_INJECTION:
if (mCurrentOperatingMode == NORMAL) {
dev.disableAllSensors();
- // Always use DATA_INJECTION here since this value goes to the HAL and the HAL
- // doesn't have an understanding of replay vs. normal data injection.
- status_t err = dev.setMode(DATA_INJECTION);
+ status_t err = NO_ERROR;
+ if (targetOperatingMode == HAL_BYPASS_REPLAY_DATA_INJECTION) {
+ // Set SensorDevice to HAL_BYPASS_REPLAY_DATA_INJECTION_MODE. This value is not
+ // injected into the HAL, nor will any events be injected into the HAL
+ err = dev.setMode(HAL_BYPASS_REPLAY_DATA_INJECTION);
+ } else {
+ // Otherwise use DATA_INJECTION here since this value goes to the HAL and the HAL
+ // doesn't have an understanding of replay vs. normal data injection.
+ err = dev.setMode(DATA_INJECTION);
+ }
if (err == NO_ERROR) {
mCurrentOperatingMode = targetOperatingMode;
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index bf43101..b643f6b 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -122,6 +122,11 @@
// delivered to all requesting apps rather than just the package allowed to inject data.
// This mode is only allowed to be used on development builds.
REPLAY_DATA_INJECTION = 3,
+ // Like REPLAY_DATA_INJECTION but injected data is not sent into the HAL. It is stored in a
+ // buffer in SensorDevice and played back to SensorService when SensorDevice::poll() is
+ // called. This is useful for playing back sensor data on the platform without relying on
+ // the HAL to support data injection.
+ HAL_BYPASS_REPLAY_DATA_INJECTION = 4,
// State Transitions supported.
// RESTRICTED <--- NORMAL ---> DATA_INJECTION/REPLAY_DATA_INJECTION
@@ -389,6 +394,8 @@
const String8& packageName,
int requestedMode, const String16& opPackageName, const String16& attributionTag);
virtual int isDataInjectionEnabled();
+ virtual int isReplayDataInjectionEnabled();
+ virtual int isHalBypassReplayDataInjectionEnabled();
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
int deviceId, uint32_t size, int32_t type, int32_t format,
const native_handle *resource);
@@ -507,6 +514,8 @@
// Removes the capped rate on active direct connections (when the mic toggle is flipped to off)
void uncapRates();
+ bool isInjectionMode(int mode);
+
static inline bool isAudioServerOrSystemServerUid(uid_t uid) {
return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
}
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 6870d4e..f6f104e 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -17,6 +17,7 @@
static_libs: [
"libsensorserviceaidl",
"libpermission",
+ "android.companion.virtual.virtualdevice_aidl-cpp",
"android.frameworks.sensorservice-V1-ndk",
"android.hardware.sensors-V1-convert",
"android.hardware.sensors-V2-ndk",
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index dcc29b9..1b1307b 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -366,6 +366,11 @@
mRenderRate = renderRate;
}
+Fps SurfaceFrame::getRenderRate() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mRenderRate ? *mRenderRate : mDisplayFrameRenderRate;
+}
+
void SurfaceFrame::setGpuComposition() {
std::scoped_lock lock(mMutex);
mGpuComposition = true;
@@ -611,9 +616,11 @@
}
void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
- nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
+ Fps displayFrameRenderRate, nsecs_t displayDeadlineDelta,
+ nsecs_t displayPresentDelta) {
std::scoped_lock lock(mMutex);
+ mDisplayFrameRenderRate = displayFrameRenderRate;
mActuals.presentTime = presentTime;
nsecs_t deadlineDelta = 0;
@@ -837,10 +844,11 @@
mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
}
-void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRate) {
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRate,
+ Fps renderRate) {
ATRACE_CALL();
std::scoped_lock lock(mMutex);
- mCurrentDisplayFrame->onSfWakeUp(token, refreshRate,
+ mCurrentDisplayFrame->onSfWakeUp(token, refreshRate, renderRate,
mTokenManager.getPredictionsForToken(token), wakeUpTime);
}
@@ -860,11 +868,12 @@
mSurfaceFrames.push_back(surfaceFrame);
}
-void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, Fps refreshRate,
+void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate,
std::optional<TimelineItem> predictions,
nsecs_t wakeUpTime) {
mToken = token;
mRefreshRate = refreshRate;
+ mRenderRate = renderRate;
if (!predictions) {
mPredictionState = PredictionState::Expired;
} else {
@@ -1026,7 +1035,8 @@
classifyJank(deadlineDelta, deltaToVsync, previousPresentTime);
for (auto& surfaceFrame : mSurfaceFrames) {
- surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
+ surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, mRenderRate, deadlineDelta,
+ deltaToVsync);
}
}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index d54d22d..538ea12 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -183,6 +183,8 @@
void setDropTime(nsecs_t dropTime);
void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
void setRenderRate(Fps renderRate);
+ // Return the render rate if it exists, otherwise returns the DisplayFrame's render rate.
+ Fps getRenderRate() const;
void setGpuComposition();
// When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
@@ -197,7 +199,8 @@
// displayRefreshRate, displayDeadlineDelta, and displayPresentDelta are propagated from the
// display frame.
void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
- nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
+ Fps displayFrameRenderRate, nsecs_t displayDeadlineDelta,
+ nsecs_t displayPresentDelta);
// All the timestamps are dumped relative to the baseTime
void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
// Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
@@ -251,6 +254,8 @@
int32_t mJankType GUARDED_BY(mMutex) = JankType::None;
// Indicates if this frame was composited by the GPU or not
bool mGpuComposition GUARDED_BY(mMutex) = false;
+ // Refresh rate for this frame.
+ Fps mDisplayFrameRenderRate GUARDED_BY(mMutex);
// Rendering rate for this frame.
std::optional<Fps> mRenderRate GUARDED_BY(mMutex);
// Enum for the type of present
@@ -298,7 +303,8 @@
// The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
// the token and sets the actualSfWakeTime for the current DisplayFrame.
- virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) = 0;
+ virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate,
+ Fps renderRate) = 0;
// Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the
// given present fence until it's signaled, and updates the present timestamps of all presented
@@ -374,8 +380,8 @@
// and SYSTEM_TIME_MONOTONIC.
void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
// Sets the token, vsyncPeriod, predictions and SF start time.
- void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
- nsecs_t wakeUpTime);
+ void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate,
+ std::optional<TimelineItem> predictions, nsecs_t wakeUpTime);
// Sets the appropriate metadata and classifies the jank.
void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
// Adds the provided SurfaceFrame to the current display frame.
@@ -437,6 +443,8 @@
// The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
// timeline
Fps mRefreshRate;
+ // The current render rate for this DisplayFrame.
+ Fps mRenderRate;
// TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto.
// Using a reference here because the counter is owned by FrameTimeline, which outlives
// DisplayFrame.
@@ -453,7 +461,7 @@
int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
GameMode) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
- void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
+ void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 80a51ea..4fd6495 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -84,6 +84,7 @@
bool isTrustedOverlay;
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate frameRate;
+ scheduler::LayerInfo::FrameRate inheritedFrameRate;
scheduler::LayerInfo::FrameRateSelectionStrategy frameRateSelectionStrategy;
scheduler::FrameRateCompatibility defaultFrameRateCompatibility =
scheduler::FrameRateCompatibility::Default;
@@ -122,6 +123,8 @@
ReachableByRelativeParent
};
Reachablilty reachablilty;
+ // True when the surfaceDamage is recognized as a small area update.
+ bool isSmallDirty = false;
static bool isOpaqueFormat(PixelFormat format);
static bool isTransformValid(const ui::Transform& t);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 03c0993..2a0857d 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -731,6 +731,10 @@
: parentSnapshot.outputFilter.layerStack;
}
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eTrustedOverlayChanged) {
+ snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
+ }
+
if (snapshot.isHiddenByPolicyFromParent &&
!snapshot.changes.test(RequestedLayerState::Changes::Created)) {
if (forceUpdate ||
@@ -761,10 +765,6 @@
(requested.flags & layer_state_t::eLayerSkipScreenshot);
}
- if (forceUpdate || snapshot.clientChanges & layer_state_t::eTrustedOverlayChanged) {
- snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
- }
-
if (forceUpdate || snapshot.clientChanges & layer_state_t::eStretchChanged) {
snapshot.stretchEffect = (requested.stretchEffect.hasEffect())
? requested.stretchEffect
@@ -815,9 +815,14 @@
RequestedLayerState::Changes::Hierarchy)) {
bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy ==
scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren;
- snapshot.frameRate = !requested.requestedFrameRate.isValid() || shouldOverrideChildren
- ? parentSnapshot.frameRate
- : requested.requestedFrameRate;
+ if (!requested.requestedFrameRate.isValid() || shouldOverrideChildren) {
+ snapshot.inheritedFrameRate = parentSnapshot.inheritedFrameRate;
+ } else {
+ snapshot.inheritedFrameRate = requested.requestedFrameRate;
+ }
+ // Set the framerate as the inherited frame rate and allow children to override it if
+ // needed.
+ snapshot.frameRate = snapshot.inheritedFrameRate;
snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 168267b..0e49b75 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -124,6 +124,7 @@
dimmingEnabled = true;
defaultFrameRateCompatibility = static_cast<int8_t>(scheduler::FrameRateCompatibility::Default);
frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default);
+ frameRateCategorySmoothSwitchOnly = false;
frameRateSelectionStrategy =
static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
dataspace = ui::Dataspace::V0_SRGB;
@@ -208,7 +209,7 @@
(barrierFrameNumber > bufferData->frameNumber))) {
ALOGE("Out of order buffers detected for %s producedId=%d frameNumber=%" PRIu64
" -> producedId=%d frameNumber=%" PRIu64,
- getDebugString().c_str(), bufferData->producerId, bufferData->frameNumber,
+ getDebugString().c_str(), barrierProducerId, barrierFrameNumber,
bufferData->producerId, frameNumber);
TransactionTraceWriter::getInstance().invoke("out_of_order_buffers_",
/*overwrite=*/false);
@@ -237,8 +238,7 @@
}
if (what & (layer_state_t::eAlphaChanged)) {
if (oldAlpha == 0 || color.a == 0) {
- changes |= RequestedLayerState::Changes::Visibility |
- RequestedLayerState::Changes::VisibleRegion;
+ changes |= RequestedLayerState::Changes::Visibility;
}
}
@@ -401,6 +401,7 @@
if (!handleAlive) debug << " !handle";
if (z != 0) debug << " z=" << z;
if (layerStack.id != 0) debug << " layerStack=" << layerStack.id;
+ debug << "}";
return debug.str();
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2dc8758..700baa2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -170,7 +170,6 @@
mDrawingState.barrierProducerId = 0;
mDrawingState.bufferTransform = 0;
mDrawingState.transformToDisplayInverse = false;
- mDrawingState.crop.makeInvalid();
mDrawingState.acquireFence = sp<Fence>::make(-1);
mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
@@ -206,6 +205,7 @@
mOwnerUid = args.ownerUid;
mOwnerPid = args.ownerPid;
+ mOwnerAppId = mOwnerUid % PER_USER_RANGE;
mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
@@ -787,8 +787,9 @@
if (includeJankData) {
std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
mPendingJankClassifications.front();
- jankData.emplace_back(
- JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
+ jankData.emplace_back(JankData(surfaceFrame->getToken(),
+ surfaceFrame->getJankType().value(),
+ surfaceFrame->getRenderRate().getPeriodNsecs()));
}
mPendingJankClassifications.pop_front();
}
@@ -1334,13 +1335,15 @@
return true;
}
-bool Layer::setFrameRateCategory(FrameRateCategory category) {
- if (mDrawingState.frameRate.category == category) {
+bool Layer::setFrameRateCategory(FrameRateCategory category, bool smoothSwitchOnly) {
+ if (mDrawingState.frameRate.category == category &&
+ mDrawingState.frameRate.categorySmoothSwitchOnly == smoothSwitchOnly) {
return false;
}
mDrawingState.sequence++;
mDrawingState.frameRate.category = category;
+ mDrawingState.frameRate.categorySmoothSwitchOnly = smoothSwitchOnly;
mDrawingState.modified = true;
updateTreeHasFrameRateVote();
@@ -2929,17 +2932,14 @@
}
}
- // Prevent tracing the same release multiple times.
- if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
- mPreviousReleasedFrameNumber = mPreviousFrameNumber;
- }
-
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
ch->name = mName;
}
- mPreviouslyPresentedLayerStacks.push_back(layerStack);
+ if (mBufferInfo.mBuffer) {
+ mPreviouslyPresentedLayerStacks.push_back(layerStack);
+ }
}
void Layer::onSurfaceFrameCreated(
@@ -3364,7 +3364,7 @@
mDrawingState.surfaceDamageRegion = surfaceDamage;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
- setIsSmallDirty();
+ setIsSmallDirty(surfaceDamage, getTransform());
return true;
}
@@ -4411,7 +4411,9 @@
mLastLatchTime = latchTime;
}
-void Layer::setIsSmallDirty() {
+void Layer::setIsSmallDirty(const Region& damageRegion,
+ const ui::Transform& layerToDisplayTransform) {
+ mSmallDirty = false;
if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
return;
}
@@ -4420,17 +4422,26 @@
mWindowType != WindowInfo::Type::BASE_APPLICATION) {
return;
}
- Rect bounds = mDrawingState.surfaceDamageRegion.getBounds();
+
+ Rect bounds = damageRegion.getBounds();
if (!bounds.isValid()) {
return;
}
+ // Transform to screen space.
+ bounds = layerToDisplayTransform.transform(bounds);
+
// If the damage region is a small dirty, this could give the hint for the layer history that
// it could suppress the heuristic rate when calculating.
- mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerUid,
+ mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId,
bounds.getWidth() * bounds.getHeight());
}
+void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) {
+ setIsSmallDirty(snapshot->surfaceDamage, snapshot->localTransform);
+ snapshot->isSmallDirty = mSmallDirty;
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 0b66866..dd91adc 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -783,7 +783,7 @@
Rect getCroppedBufferSize(const Layer::State& s) const;
bool setFrameRate(FrameRate::FrameRateVote);
- bool setFrameRateCategory(FrameRateCategory);
+ bool setFrameRateCategory(FrameRateCategory, bool smoothSwitchOnly);
bool setFrameRateSelectionStrategy(FrameRateSelectionStrategy);
@@ -837,6 +837,8 @@
pid_t getOwnerPid() { return mOwnerPid; }
+ int32_t getOwnerAppId() { return mOwnerAppId; }
+
// This layer is not a clone, but it's the parent to the cloned hierarchy. The
// variable mClonedChild represents the top layer that will be cloned so this
// layer will be the parent of mClonedChild.
@@ -935,7 +937,8 @@
const sp<SurfaceFlinger> mFlinger;
// Check if the damage region is a small dirty.
- void setIsSmallDirty();
+ void setIsSmallDirty(const Region& damageRegion, const ui::Transform& layerToDisplayTransform);
+ void setIsSmallDirty(frontend::LayerSnapshot* snapshot);
protected:
// For unit tests
@@ -1062,6 +1065,8 @@
// If created from a system process, the value can be passed in.
pid_t mOwnerPid;
+ int32_t mOwnerAppId;
+
// Keeps track of the time SF latched the last buffer from this layer.
// Used in buffer stuffing analysis in FrameTimeline.
nsecs_t mLastLatchTime = 0;
@@ -1071,6 +1076,10 @@
sp<Fence> mLastClientCompositionFence;
bool mClearClientCompositionFenceOnLayerDisplayed = false;
private:
+ // Range of uids allocated for a user.
+ // This value is taken from android.os.UserHandle#PER_USER_RANGE.
+ static constexpr int32_t PER_USER_RANGE = 100000;
+
friend class SlotGenerationTest;
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
@@ -1227,9 +1236,6 @@
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
sp<IBinder> mPreviousReleaseBufferEndpoint;
- uint64_t mPreviousReleasedFrameNumber = 0;
-
- uint64_t mPreviousBarrierFrameNumber = 0;
bool mReleasePreviousBuffer = false;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 48a9190..f25619a 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -247,10 +247,13 @@
layerSettings.frameNumber = mSnapshot->frameNumber;
layerSettings.bufferId = mSnapshot->externalTexture->getId();
+ const bool useFiltering = targetSettings.needsFiltering ||
+ mSnapshot->geomLayerTransform.needsBilinearFiltering();
+
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
getDrawingTransformMatrix(layerSettings.source.buffer.buffer, mSnapshot->geomContentCrop,
- mSnapshot->geomBufferTransform, targetSettings.needsFiltering,
+ mSnapshot->geomBufferTransform, useFiltering,
textureMatrix);
if (mSnapshot->geomBufferUsesDisplayInverseTransform) {
@@ -301,7 +304,7 @@
mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
- layerSettings.source.buffer.useTextureFiltering = targetSettings.needsFiltering;
+ layerSettings.source.buffer.useTextureFiltering = useFiltering;
layerSettings.source.buffer.textureTransform =
mat4(static_cast<const float*>(textureMatrix)) * tr;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 4e5659e..069d89b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -194,7 +194,8 @@
to_string(vote.fps).c_str(), categoryString.c_str(),
weight * 100);
summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
- vote.seamlessness, vote.category, weight, layerFocused});
+ vote.seamlessness, vote.category, vote.categorySmoothSwitchOnly,
+ weight, layerFocused});
if (CC_UNLIKELY(mTraceEnabled)) {
trace(*info, vote.type, vote.fps.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index dd96930..36f2475 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -309,7 +309,8 @@
ALOGV("%s uses frame rate category: %d", mName.c_str(),
static_cast<int>(mLayerVote.category));
votes.push_back({LayerHistory::LayerVoteType::ExplicitCategory, Fps(),
- Seamlessness::Default, mLayerVote.category});
+ Seamlessness::Default, mLayerVote.category,
+ mLayerVote.categorySmoothSwitchOnly});
}
if (mLayerVote.fps.isValid() ||
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index d580b58..7d3cffa 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -71,8 +71,8 @@
LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
Fps fps;
Seamlessness seamlessness = Seamlessness::Default;
- // Category is in effect if fps is not specified.
FrameRateCategory category = FrameRateCategory::Default;
+ bool categorySmoothSwitchOnly = false;
// Returns true if the layer explicitly should contribute to frame rate scoring.
bool isNoVote() const { return RefreshRateSelector::isNoVote(type); }
@@ -111,6 +111,7 @@
} vote;
FrameRateCategory category = FrameRateCategory::Default;
+ bool categorySmoothSwitchOnly = false;
FrameRate() = default;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index b06723d..1d23fb5 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -494,6 +494,7 @@
int explicitExact = 0;
int explicitCategoryVoteLayers = 0;
int seamedFocusedLayers = 0;
+ int categorySmoothSwitchOnlyLayers = 0;
for (const auto& layer : layers) {
switch (layer.vote) {
@@ -531,6 +532,9 @@
if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
seamedFocusedLayers++;
}
+ if (layer.frameRateCategorySmoothSwitchOnly) {
+ categorySmoothSwitchOnlyLayers++;
+ }
}
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
@@ -578,10 +582,17 @@
return {ranking, kNoSignals};
}
+ const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0;
+ const DisplayModeId activeModeId = activeMode.getId();
+
// Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) {
ALOGV("All layers Min");
- const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending);
+ const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending,
+ std::nullopt, [&](FrameRateMode mode) {
+ return !smoothSwitchOnly ||
+ mode.modePtr->getId() == activeModeId;
+ });
ATRACE_FORMAT_INSTANT("%s (All layers Min)",
to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
@@ -627,6 +638,14 @@
continue;
}
+ if (smoothSwitchOnly && modePtr->getId() != activeModeId) {
+ ALOGV("%s ignores %s because it's non-VRR and smooth switch only."
+ " Current mode = %s",
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+ to_string(activeMode).c_str());
+ continue;
+ }
+
// Layers with default seamlessness vote for the current mode group if
// there are layers with seamlessness=SeamedAndSeamless and for the default
// mode group otherwise. In second case, if the current mode group is different
@@ -770,6 +789,7 @@
to_string(descending.front().frameRateMode.fps).c_str());
return {descending, kNoSignals};
} else {
+ ALOGV("primaryRangeIsSingleRate");
ATRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
@@ -805,6 +825,7 @@
// If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
// current config
if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
+ ALOGV("preferredDisplayMode");
const auto ascendingWithPreferred =
rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
ATRACE_FORMAT_INSTANT("%s (preferredDisplayMode)",
@@ -812,6 +833,7 @@
return {ascendingWithPreferred, kNoSignals};
}
+ ALOGV("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str());
ATRACE_FORMAT_INSTANT("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
}
@@ -1017,7 +1039,8 @@
auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
RefreshRateOrder refreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt) const
+ std::optional<DisplayModeId> preferredDisplayModeOpt,
+ const RankFrameRatesPredicate& predicate) const
-> FrameRateRanking {
using fps_approx_ops::operator<;
const char* const whence = __func__;
@@ -1044,7 +1067,8 @@
std::deque<ScoredFrameRate> ranking;
const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
const auto& modePtr = frameRateMode.modePtr;
- if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
+ if ((anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) ||
+ !predicate(frameRateMode)) {
return;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 5d32414..545b939 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -170,8 +170,11 @@
Fps desiredRefreshRate;
// If a seamless mode switch is required.
Seamlessness seamlessness = Seamlessness::Default;
- // Layer frame rate category. Superseded by desiredRefreshRate.
+ // Layer frame rate category.
FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+ // Goes together with frame rate category vote. Allow refresh rate changes only
+ // if there would be no jank.
+ bool frameRateCategorySmoothSwitchOnly = false;
// Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
// would have on choosing the refresh rate.
float weight = 0.0f;
@@ -446,10 +449,15 @@
ftl_last = Descending
};
- // Only uses the primary range, not the app request range.
+ typedef std::function<bool(const FrameRateMode)> RankFrameRatesPredicate;
+
+ // Rank the frame rates.
+ // Only modes in the primary range for which `predicate` is `true` will be scored.
+ // Does not use the app requested range.
FrameRateRanking rankFrameRates(
std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const
+ std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt,
+ const RankFrameRatesPredicate& predicate = [](FrameRateMode) { return true; }) const
REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5115c42..5b36a5e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -1198,18 +1198,19 @@
}
void Scheduler::updateSmallAreaDetection(
- std::vector<std::pair<uid_t, float>>& uidThresholdMappings) {
+ std::vector<std::pair<int32_t, float>>& uidThresholdMappings) {
mSmallAreaDetectionAllowMappings.update(uidThresholdMappings);
}
-void Scheduler::setSmallAreaDetectionThreshold(uid_t uid, float threshold) {
- mSmallAreaDetectionAllowMappings.setThesholdForUid(uid, threshold);
+void Scheduler::setSmallAreaDetectionThreshold(int32_t appId, float threshold) {
+ mSmallAreaDetectionAllowMappings.setThesholdForAppId(appId, threshold);
}
-bool Scheduler::isSmallDirtyArea(uid_t uid, uint32_t dirtyArea) {
- std::optional<float> oThreshold = mSmallAreaDetectionAllowMappings.getThresholdForUid(uid);
- if (oThreshold) return mLayerHistory.isSmallDirtyArea(dirtyArea, oThreshold.value());
-
+bool Scheduler::isSmallDirtyArea(int32_t appId, uint32_t dirtyArea) {
+ std::optional<float> oThreshold = mSmallAreaDetectionAllowMappings.getThresholdForAppId(appId);
+ if (oThreshold) {
+ return mLayerHistory.isSmallDirtyArea(dirtyArea, oThreshold.value());
+ }
return false;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3441318..b0520a6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -293,12 +293,12 @@
void setGameModeRefreshRateForUid(FrameRateOverride);
- void updateSmallAreaDetection(std::vector<std::pair<uid_t, float>>& uidThresholdMappings);
+ void updateSmallAreaDetection(std::vector<std::pair<int32_t, float>>& uidThresholdMappings);
- void setSmallAreaDetectionThreshold(uid_t uid, float threshold);
+ void setSmallAreaDetectionThreshold(int32_t appId, float threshold);
// Returns true if the dirty area is less than threshold.
- bool isSmallDirtyArea(uid_t uid, uint32_t dirtyArea);
+ bool isSmallDirtyArea(int32_t appId, uint32_t dirtyArea);
// Retrieves the overridden refresh rate for a given uid.
std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
diff --git a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
index 95cd5d1..38c6da4 100644
--- a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
+++ b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
@@ -19,26 +19,26 @@
namespace android::scheduler {
void SmallAreaDetectionAllowMappings::update(
- std::vector<std::pair<uid_t, float>>& uidThresholdMappings) {
+ std::vector<std::pair<int32_t, float>>& appIdThresholdMappings) {
std::lock_guard lock(mLock);
mMap.clear();
- for (std::pair<uid_t, float> row : uidThresholdMappings) {
+ for (std::pair<int32_t, float> row : appIdThresholdMappings) {
if (!isValidThreshold(row.second)) continue;
mMap.emplace(row.first, row.second);
}
}
-void SmallAreaDetectionAllowMappings::setThesholdForUid(uid_t uid, float threshold) {
+void SmallAreaDetectionAllowMappings::setThesholdForAppId(int32_t appId, float threshold) {
if (!isValidThreshold(threshold)) return;
std::lock_guard lock(mLock);
- mMap.emplace(uid, threshold);
+ mMap.emplace(appId, threshold);
}
-std::optional<float> SmallAreaDetectionAllowMappings::getThresholdForUid(uid_t uid) {
+std::optional<float> SmallAreaDetectionAllowMappings::getThresholdForAppId(int32_t appId) {
std::lock_guard lock(mLock);
- const auto iter = mMap.find(uid);
+ const auto iter = mMap.find(appId);
if (iter != mMap.end()) {
return iter->second;
}
diff --git a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
index cbab690..e10301c 100644
--- a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
+++ b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
@@ -24,16 +24,16 @@
namespace android::scheduler {
class SmallAreaDetectionAllowMappings {
- using UidThresholdMap = std::unordered_map<uid_t, float>;
+ using AppIdThresholdMap = std::unordered_map<int32_t, float>;
public:
- void update(std::vector<std::pair<uid_t, float>>& uidThresholdMappings);
- void setThesholdForUid(uid_t uid, float threshold) EXCLUDES(mLock);
- std::optional<float> getThresholdForUid(uid_t uid) EXCLUDES(mLock);
+ void update(std::vector<std::pair<int32_t, float>>& appIdThresholdMappings);
+ void setThesholdForAppId(int32_t appId, float threshold) EXCLUDES(mLock);
+ std::optional<float> getThresholdForAppId(int32_t uid) EXCLUDES(mLock);
private:
static bool isValidThreshold(float threshold) { return threshold >= 0.0f && threshold <= 1.0f; }
mutable std::mutex mLock;
- UidThresholdMap mMap GUARDED_BY(mLock);
+ AppIdThresholdMap mMap GUARDED_BY(mLock);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index c4c9fa5..f467670 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -40,16 +40,18 @@
namespace {
-nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
+nsecs_t getExpectedCallbackTime(nsecs_t now, nsecs_t nextVsyncTime,
const VSyncDispatch::ScheduleTiming& timing) {
- return nextVsyncTime - timing.readyDuration - timing.workDuration;
+ const auto expectedCallbackTime = nextVsyncTime - timing.readyDuration - timing.workDuration;
+ const auto baseTime = flags::dont_skip_on_early() ? now : expectedCallbackTime;
+ return std::max(baseTime, expectedCallbackTime);
}
nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
const VSyncDispatch::ScheduleTiming& timing) {
const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
- return getExpectedCallbackTime(nextVsyncTime, timing);
+ return getExpectedCallbackTime(now, nextVsyncTime, timing);
}
} // namespace
@@ -105,11 +107,11 @@
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
if (flags::dont_skip_on_early()) {
if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
- return getExpectedCallbackTime(mArmedInfo->mActualVsyncTime, timing);
+ return getExpectedCallbackTime(now, mArmedInfo->mActualVsyncTime, timing);
}
} else {
if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
- return getExpectedCallbackTime(nextVsyncTime, timing);
+ return getExpectedCallbackTime(now, nextVsyncTime, timing);
}
}
@@ -119,7 +121,7 @@
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
- return getExpectedCallbackTime(nextVsyncTime, timing);
+ return getExpectedCallbackTime(now, nextVsyncTime, timing);
}
void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Timer.h b/services/surfaceflinger/Scheduler/include/scheduler/Timer.h
index 58ad6cb..67f7abe 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Timer.h
@@ -60,6 +60,7 @@
void reset() EXCLUDES(mMutex);
void cleanup() REQUIRES(mMutex);
void setDebugState(DebugState) EXCLUDES(mMutex);
+ void setCallback(std::function<void()>&&) REQUIRES(mMutex);
int mTimerFd = -1;
@@ -71,6 +72,7 @@
void endDispatch();
mutable std::mutex mMutex;
+
std::function<void()> mCallback GUARDED_BY(mMutex);
bool mExpectingCallback GUARDED_BY(mMutex) = false;
DebugState mDebugState GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
index a4cf57f..09e8a1e 100644
--- a/services/surfaceflinger/Scheduler/src/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -93,8 +93,8 @@
close(mPipes[kWritePipe]);
mPipes[kWritePipe] = -1;
}
- mExpectingCallback = false;
- mCallback = {};
+
+ setCallback({});
}
void Timer::endDispatch() {
@@ -112,8 +112,7 @@
static constexpr int ns_per_s =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
- mCallback = std::move(callback);
- mExpectingCallback = true;
+ setCallback(std::move(callback));
struct itimerspec old_timer;
struct itimerspec new_timer {
@@ -142,6 +141,8 @@
if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
ALOGW("Failed to disarm timerfd");
}
+
+ setCallback({});
}
void Timer::threadMain() {
@@ -231,6 +232,11 @@
mDebugState = state;
}
+void Timer::setCallback(std::function<void()>&& callback) {
+ mExpectingCallback = bool(callback);
+ mCallback = std::move(callback);
+}
+
void Timer::dump(std::string& result) const {
std::lock_guard lock(mMutex);
result.append("\t\tDebugState: ");
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7e799bb..9b9a67a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -864,14 +864,14 @@
mPowerAdvisor->init();
- char primeShaderCache[PROPERTY_VALUE_MAX];
- property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
- if (atoi(primeShaderCache)) {
+ if (base::GetBoolProperty("service.sf.prime_shader_cache"s, true)) {
if (setSchedFifo(false) != NO_ERROR) {
ALOGW("Can't set SCHED_OTHER for primeCache");
}
- mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache();
+ bool shouldPrimeUltraHDR =
+ base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
+ mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache(shouldPrimeUltraHDR);
if (setSchedFifo(true) != NO_ERROR) {
ALOGW("Can't set SCHED_OTHER for primeCache");
@@ -1047,6 +1047,7 @@
const auto peakFps = mode->getPeakFps();
outMode.refreshRate = peakFps.getValue();
+ outMode.vsyncRate = mode->getVsyncRate().getValue();
const auto vsyncConfigSet =
mVsyncConfiguration->getConfigsForRefreshRate(Fps::fromValue(outMode.refreshRate));
@@ -1231,7 +1232,7 @@
}
status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
- DisplayModeId modeId) {
+ DisplayModeId modeId, Fps minFps, Fps maxFps) {
ATRACE_CALL();
if (!displayToken) {
@@ -1264,13 +1265,15 @@
}
const Fps fps = *fpsOpt;
+ const FpsRange physical = {fps, fps};
+ const FpsRange render = {minFps.isValid() ? minFps : fps, maxFps.isValid() ? maxFps : fps};
+ const FpsRanges ranges = {physical, render};
// Keep the old switching type.
const bool allowGroupSwitching =
display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
- const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId,
- {fps, fps},
+ const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId, ranges, ranges,
allowGroupSwitching};
return setDesiredDisplayModeSpecsInternal(display, policy);
@@ -2210,8 +2213,29 @@
continue;
}
- if (!snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
- (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
+ const bool updateSmallDirty = mScheduler->supportSmallDirtyDetection() &&
+ ((snapshot->clientChanges & layer_state_t::eSurfaceDamageRegionChanged) ||
+ snapshot->changes.any(Changes::Geometry));
+
+ const bool hasChanges =
+ snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) ||
+ (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) !=
+ 0;
+
+ if (!updateSmallDirty && !hasChanges) {
+ continue;
+ }
+
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldn't find layer object for %s",
+ snapshot->getDebugString().c_str());
+
+ if (updateSmallDirty) {
+ // Update small dirty flag while surface damage region or geometry changed
+ it->second->setIsSmallDirty(snapshot.get());
+ }
+
+ if (!hasChanges) {
continue;
}
@@ -2221,12 +2245,9 @@
.transform = snapshot->geomLayerTransform,
.setFrameRateVote = snapshot->frameRate,
.frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
+ .isSmallDirty = snapshot->isSmallDirty,
};
- auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
-
if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
snapshot->defaultFrameRateCompatibility);
@@ -2400,8 +2421,6 @@
mLayerLifecycleManager.commitChanges();
}
- commitTransactions();
-
// enter boot animation on first buffer latch
if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
ALOGI("Enter boot animation");
@@ -2409,6 +2428,10 @@
}
}
mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched;
+ if (mustComposite && !mLegacyFrontEndEnabled) {
+ commitTransactions();
+ }
+
return mustComposite;
}
@@ -2499,7 +2522,8 @@
{
mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId),
pacesetterFrameTarget.frameBeginTime().ns(),
- Fps::fromPeriodNsecs(vsyncPeriod.ns()));
+ Fps::fromPeriodNsecs(vsyncPeriod.ns()),
+ mScheduler->getPacesetterRefreshRate());
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
bool transactionsAreEmpty;
@@ -4027,7 +4051,7 @@
if (sysprop::use_content_detection_for_refresh_rate(false)) {
features |= Feature::kContentDetection;
- if (flags::vrr_small_dirty_detection()) {
+ if (flags::enable_small_area_detection()) {
features |= Feature::kSmallDirtyContentDetection;
}
}
@@ -5228,7 +5252,7 @@
}
if (what & layer_state_t::eFrameRateCategoryChanged) {
const FrameRateCategory category = Layer::FrameRate::convertCategory(s.frameRateCategory);
- if (layer->setFrameRateCategory(category)) {
+ if (layer->setFrameRateCategory(category, s.frameRateCategorySmoothSwitchOnly)) {
flags |= eTraversalNeeded;
}
}
@@ -5668,7 +5692,6 @@
state.id = transactionId;
// reset screen orientation and use primary layer stack
- Vector<DisplayState> displays;
DisplayState d;
d.what = DisplayState::eDisplayProjectionChanged |
DisplayState::eLayerStackChanged;
@@ -6929,6 +6952,12 @@
return NO_ERROR;
}
case 1035: {
+ // Parameters:
+ // - (required) i32 mode id.
+ // - (optional) i64 display id. Using default display if not provided.
+ // - (optional) f min render rate. Using mode's fps is not provided.
+ // - (optional) f max render rate. Using mode's fps is not provided.
+
const int modeId = data.readInt32();
const auto display = [&]() -> sp<IBinder> {
@@ -6945,8 +6974,21 @@
return nullptr;
}();
+ const auto getFps = [&] {
+ float value;
+ if (data.readFloat(&value) == NO_ERROR) {
+ return Fps::fromValue(value);
+ }
+
+ return Fps();
+ };
+
+ const auto minFps = getFps();
+ const auto maxFps = getFps();
+
mDebugDisplayModeSetByBackdoor = false;
- const status_t result = setActiveModeFromBackdoor(display, DisplayModeId{modeId});
+ const status_t result =
+ setActiveModeFromBackdoor(display, DisplayModeId{modeId}, minFps, maxFps);
mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
return result;
}
@@ -7059,7 +7101,7 @@
if (mTransactionTracing) {
int arg = data.readInt32();
if (arg == -1) {
- mTransactionTracing.reset();
+ mScheduler->schedule([&]() { mTransactionTracing.reset(); }).get();
} else if (arg > 0) {
// Transaction tracing is always running but allow the user to temporarily
// increase the buffer when actively debugging.
@@ -8342,13 +8384,13 @@
}
status_t SurfaceFlinger::updateSmallAreaDetection(
- std::vector<std::pair<uid_t, float>>& uidThresholdMappings) {
- mScheduler->updateSmallAreaDetection(uidThresholdMappings);
+ std::vector<std::pair<int32_t, float>>& appIdThresholdMappings) {
+ mScheduler->updateSmallAreaDetection(appIdThresholdMappings);
return NO_ERROR;
}
-status_t SurfaceFlinger::setSmallAreaDetectionThreshold(uid_t uid, float threshold) {
- mScheduler->setSmallAreaDetectionThreshold(uid, threshold);
+status_t SurfaceFlinger::setSmallAreaDetectionThreshold(int32_t appId, float threshold) {
+ mScheduler->setSmallAreaDetectionThreshold(appId, threshold);
return NO_ERROR;
}
@@ -8489,15 +8531,6 @@
void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
mScheduler->onActiveDisplayAreaChanged(activeDisplay.getWidth() * activeDisplay.getHeight());
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
-
- // Notify layers to update small dirty flag.
- if (mScheduler->supportSmallDirtyDetection()) {
- mCurrentState.traverse([&](Layer* layer) {
- if (layer->getLayerStack() == activeDisplay.getLayerStack()) {
- layer->setIsSmallDirty();
- }
- });
- }
}
sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const {
@@ -9189,6 +9222,7 @@
outMode.xDpi = mode.xDpi;
outMode.yDpi = mode.yDpi;
outMode.refreshRate = mode.refreshRate;
+ outMode.vsyncRate = mode.vsyncRate;
outMode.appVsyncOffset = mode.appVsyncOffset;
outMode.sfVsyncOffset = mode.sfVsyncOffset;
outMode.presentationDeadline = mode.presentationDeadline;
@@ -9719,18 +9753,18 @@
return binder::Status::ok();
}
-binder::Status SurfaceComposerAIDL::updateSmallAreaDetection(const std::vector<int32_t>& uids,
+binder::Status SurfaceComposerAIDL::updateSmallAreaDetection(const std::vector<int32_t>& appIds,
const std::vector<float>& thresholds) {
status_t status;
const int c_uid = IPCThreadState::self()->getCallingUid();
if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
- if (uids.size() != thresholds.size()) return binderStatusFromStatusT(BAD_VALUE);
+ if (appIds.size() != thresholds.size()) return binderStatusFromStatusT(BAD_VALUE);
- std::vector<std::pair<uid_t, float>> mappings;
- const size_t size = uids.size();
+ std::vector<std::pair<int32_t, float>> mappings;
+ const size_t size = appIds.size();
mappings.reserve(size);
for (int i = 0; i < size; i++) {
- auto row = std::make_pair(static_cast<uid_t>(uids[i]), thresholds[i]);
+ auto row = std::make_pair(appIds[i], thresholds[i]);
mappings.push_back(row);
}
status = mFlinger->updateSmallAreaDetection(mappings);
@@ -9741,11 +9775,11 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::setSmallAreaDetectionThreshold(int32_t uid, float threshold) {
+binder::Status SurfaceComposerAIDL::setSmallAreaDetectionThreshold(int32_t appId, float threshold) {
status_t status;
const int c_uid = IPCThreadState::self()->getCallingUid();
if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
- status = mFlinger->setSmallAreaDetectionThreshold(uid, threshold);
+ status = mFlinger->setSmallAreaDetectionThreshold(appId, threshold);
} else {
ALOGE("setSmallAreaDetectionThreshold() permission denied for uid: %d", c_uid);
status = PERMISSION_DENIED;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index cbea312..96b67b8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -606,9 +606,9 @@
status_t setOverrideFrameRate(uid_t uid, float frameRate);
- status_t updateSmallAreaDetection(std::vector<std::pair<uid_t, float>>& uidThresholdMappings);
+ status_t updateSmallAreaDetection(std::vector<std::pair<int32_t, float>>& uidThresholdMappings);
- status_t setSmallAreaDetectionThreshold(uid_t uid, float threshold);
+ status_t setSmallAreaDetectionThreshold(int32_t appId, float threshold);
int getGpuContextPriority();
@@ -684,7 +684,8 @@
void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
REQUIRES(mStateLock);
- status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
+ status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
+ Fps maxFps);
void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
@@ -728,8 +729,10 @@
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly);
void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
const std::vector<std::pair<Layer*, LayerFE*>>& layers);
+ // Return true if we must composite this frame
bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
+ // Return true if we must composite this frame
bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
void updateLayerHistory(nsecs_t now);
@@ -1572,9 +1575,9 @@
binder::Status setDebugFlash(int delay) override;
binder::Status scheduleComposite() override;
binder::Status scheduleCommit() override;
- binder::Status updateSmallAreaDetection(const std::vector<int32_t>& uids,
+ binder::Status updateSmallAreaDetection(const std::vector<int32_t>& appIds,
const std::vector<float>& thresholds) override;
- binder::Status setSmallAreaDetectionThreshold(int32_t uid, float threshold) override;
+ binder::Status setSmallAreaDetectionThreshold(int32_t appId, float threshold) override;
binder::Status getGpuContextPriority(int32_t* outPriority) override;
binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
binder::Status addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 922fd07..f339d22 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -40,5 +40,10 @@
{
"name": "libsurfaceflinger_unittest"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsSurfaceControlTestsStaging"
+ }
]
}
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index 698ef06..18022b1 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -64,8 +64,17 @@
if (!LayerTraceGenerator().generate(transactionTraceFile, traceFlags, layerTracing,
generateLastEntryOnly)) {
- std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
+ std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath << "\n";
return -1;
}
+
+ // Set output file permissions (-rw-r--r--)
+ outStream.close();
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (chmod(outputLayersTracePath, mode) != 0) {
+ std::cout << "Error: Failed to set permissions of " << outputLayersTracePath << "\n";
+ return -1;
+ }
+
return 0;
}
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 5a277bd..19d194f 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -32,14 +32,6 @@
}
flag {
- name: "vrr_small_dirty_detection"
- namespace: "core_graphics"
- description: "Controls small dirty detection for VRR"
- bug: "283055450"
- is_fixed_read_only: true
-}
-
-flag {
name: "dont_skip_on_early"
namespace: "core_graphics"
description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
@@ -53,3 +45,11 @@
bug: "259132483"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_small_area_detection"
+ namespace: "core_graphics"
+ description: "Feature flag for SmallAreaDetection"
+ bug: "283055450"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
index f2874ae..4f41a81 100644
--- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -28,33 +28,95 @@
using gui::aidl_utils::statusTFromBinderStatus;
-TEST(BootDisplayModeTest, setBootDisplayMode) {
- sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+struct BootDisplayModeTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mSf = ComposerServiceAIDL::getComposerService();
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- bool bootModeSupport = false;
- binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
- ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
- if (bootModeSupport) {
- status = sf->setBootDisplayMode(displayToken, 0);
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+ bool bootModeSupport = false;
+ binder::Status status = mSf->getBootDisplayModeSupport(&bootModeSupport);
+ ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
+
+ if (!bootModeSupport) {
+ GTEST_SKIP() << "Boot mode not supported";
+ }
+
+ gui::DynamicDisplayInfo info;
+ status = mSf->getDynamicDisplayInfoFromToken(mDisplayToken, &info);
ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ mOldMode = info.preferredBootDisplayMode;
+ const auto newMode = [&]() -> std::optional<ui::DisplayModeId> {
+ for (const auto& mode : info.supportedDisplayModes) {
+ if (mode.id != mOldMode) {
+ return std::optional(mode.id);
+ }
+ }
+ return std::nullopt;
+ }();
+
+ if (!newMode) {
+ GTEST_SKIP() << "Only a single mode is supported";
+ }
+
+ mNewMode = *newMode;
}
+
+ void TearDown() override {
+ binder::Status status = mSf->setBootDisplayMode(mDisplayToken, mOldMode);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+
+ gui::DynamicDisplayInfo info;
+ status = mSf->getDynamicDisplayInfoFromToken(mDisplayToken, &info);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ EXPECT_EQ(mOldMode, info.preferredBootDisplayMode);
+ }
+
+ ui::DisplayModeId mOldMode;
+ ui::DisplayModeId mNewMode;
+ sp<gui::ISurfaceComposer> mSf;
+ sp<IBinder> mDisplayToken;
+};
+
+TEST_F(BootDisplayModeTest, setBootDisplayMode) {
+ // Set a new mode and check that it got applied
+ binder::Status status = mSf->setBootDisplayMode(mDisplayToken, mNewMode);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+
+ gui::DynamicDisplayInfo info;
+ status = mSf->getDynamicDisplayInfoFromToken(mDisplayToken, &info);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ EXPECT_EQ(mNewMode, info.preferredBootDisplayMode);
}
-TEST(BootDisplayModeTest, clearBootDisplayMode) {
- sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- bool bootModeSupport = false;
- binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
- ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
- if (bootModeSupport) {
- status = sf->clearBootDisplayMode(displayToken);
- ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
- }
+TEST_F(BootDisplayModeTest, clearBootDisplayMode) {
+ // Clear once to figure out what the system default is
+ binder::Status status = mSf->clearBootDisplayMode(mDisplayToken);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+
+ gui::DynamicDisplayInfo info;
+ status = mSf->getDynamicDisplayInfoFromToken(mDisplayToken, &info);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+
+ const ui::DisplayModeId systemMode = info.preferredBootDisplayMode;
+ const ui::DisplayModeId newMode = systemMode == mOldMode ? mNewMode : mOldMode;
+
+ // Now set a new mode and clear the boot mode again to figure out if the api worked.
+ status = mSf->setBootDisplayMode(mDisplayToken, newMode);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+
+ status = mSf->getDynamicDisplayInfoFromToken(mDisplayToken, &info);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ EXPECT_EQ(newMode, info.preferredBootDisplayMode);
+
+ status = mSf->clearBootDisplayMode(mDisplayToken);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+
+ status = mSf->getDynamicDisplayInfoFromToken(mDisplayToken, &info);
+ EXPECT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ EXPECT_EQ(systemMode, info.preferredBootDisplayMode);
}
} // namespace android
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 69e9a16..822ac4d 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -275,18 +275,6 @@
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
}
-TEST_F(CredentialsTest, CaptureTest) {
- const auto display = getFirstDisplayToken();
- std::function<status_t()> condition = [=]() {
- sp<GraphicBuffer> outBuffer;
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = display;
- ScreenCaptureResults captureResults;
- return ScreenCapture::captureDisplay(captureArgs, captureResults);
- };
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
-}
-
TEST_F(CredentialsTest, CaptureLayersTest) {
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 34c9182..f9b4bba 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -167,18 +167,18 @@
.setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
.apply(true);
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
+ LayerCaptureArgs args;
+ args.layerHandle = layer->getHandle();
ScreenCaptureResults captureResults;
{
// Ensure the UID is not root because root has all permissions
UIDFaker f(AID_APP_START);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
}
Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
}
TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 91910c7..5a3bca1 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,8 +45,7 @@
cc_aconfig_library {
name: "libsurfaceflingerflags_test",
aconfig_declarations: "surfaceflinger_flags",
- // TODO(b/304338314): uncomment the below line once the bug is fixed
- // test: true,
+ test: true,
}
cc_test {
@@ -64,6 +63,7 @@
"libsurfaceflinger_unittest_main.cpp",
"ActiveDisplayRotationFlagsTest.cpp",
"BackgroundExecutorTest.cpp",
+ "CommitTest.cpp",
"CompositionTest.cpp",
"DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CommitTest.cpp b/services/surfaceflinger/tests/unittests/CommitTest.cpp
new file mode 100644
index 0000000..df53d19
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/CommitTest.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "CommitTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <renderengine/mock/RenderEngine.h>
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+class CommitTest : public testing::Test {
+protected:
+ CommitTest() {
+ mFlinger.setupMockScheduler();
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ }
+ TestableSurfaceFlinger mFlinger;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+namespace {
+
+TEST_F(CommitTest, noUpdatesDoesNotScheduleComposite) {
+ bool unused;
+ bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0,
+ /*transactionsFlushed=*/0, unused);
+ EXPECT_FALSE(mustComposite);
+}
+
+// Ensure that we handle eTransactionNeeded correctly
+TEST_F(CommitTest, eTransactionNeededFlagSchedulesComposite) {
+ // update display level color matrix
+ mFlinger.setDaltonizerType(ColorBlindnessType::Deuteranomaly);
+ bool unused;
+ bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0,
+ /*transactionsFlushed=*/0, unused);
+ EXPECT_TRUE(mustComposite);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 8911430..636d852 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -54,6 +54,8 @@
constexpr int32_t sLayerIdOne = 1;
constexpr int32_t sLayerIdTwo = 2;
constexpr GameMode sGameMode = GameMode::Unsupported;
+constexpr Fps RR_11 = Fps::fromPeriodNsecs(11);
+constexpr Fps RR_30 = Fps::fromPeriodNsecs(30);
class FrameTimelineTest : public testing::Test {
public:
@@ -276,7 +278,7 @@
/*isBuffer*/ true, sGameMode);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(token1, 20, RR_11, RR_11);
surfaceFrame1->setDropTime(12);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -309,7 +311,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdTwo,
sLayerNameTwo, sLayerNameTwo,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -354,7 +356,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, RR_11, RR_11);
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
@@ -379,7 +381,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, RR_11, RR_11);
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
@@ -422,7 +424,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken, 22, RR_11, RR_11);
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
@@ -439,7 +441,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken, 22, RR_11, RR_11);
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
@@ -456,7 +458,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken, 22, RR_11, RR_11);
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
@@ -465,7 +467,7 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_invalidSignalTime) {
- Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps refreshRate = RR_11;
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
@@ -478,7 +480,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -496,7 +498,7 @@
// Tests related to TimeStats
TEST_F(FrameTimelineTest, presentFenceSignaled_doesNotReportForInvalidTokens) {
- Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps refreshRate = RR_11;
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(0);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = -1;
@@ -509,7 +511,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -519,7 +521,7 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
- Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps refreshRate = RR_11;
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
@@ -537,7 +539,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -547,7 +549,7 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) {
- Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps refreshRate = RR_11;
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
@@ -566,7 +568,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -577,7 +579,7 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
- Fps refreshRate = Fps::fromPeriodNsecs(30);
+ Fps refreshRate = RR_30;
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
sLayerNameOne, sGameMode,
@@ -594,7 +596,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setAcquireFenceTime(20);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -622,7 +624,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(45);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -651,7 +653,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(50);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -680,7 +682,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(40);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -709,7 +711,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(40);
- mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented,
/*previousLatchTime*/ 56);
@@ -721,8 +723,8 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
- Fps refreshRate = Fps::fromPeriodNsecs(11);
- Fps renderRate = Fps::fromPeriodNsecs(30);
+ Fps refreshRate = RR_11;
+ Fps renderRate = RR_30;
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne,
sLayerNameOne, sGameMode,
@@ -740,7 +742,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(45);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setRenderRate(renderRate);
@@ -752,8 +754,8 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPresentsSurfaceFrame) {
- Fps refreshRate = Fps::fromPeriodNsecs(11);
- Fps renderRate = Fps::fromPeriodNsecs(30);
+ Fps refreshRate = RR_11;
+ Fps renderRate = RR_30;
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
@@ -775,7 +777,7 @@
surfaceFrame1->setAcquireFenceTime(45);
// Trigger a prediction expiry
flushTokens();
- mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setRenderRate(renderRate);
@@ -814,7 +816,7 @@
/*isBuffer*/ true, sGameMode);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(token1, 20, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
@@ -844,7 +846,7 @@
/*isBuffer*/ true, sGameMode);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(token2, 20, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
@@ -866,7 +868,7 @@
tracingSession->StartBlocking();
// Set up the display frame
- mFrameTimeline->setSfWakeUp(-1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(-1, 20, RR_11, RR_11);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
@@ -890,7 +892,7 @@
/*isBuffer*/ true, sGameMode);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(token1, 20, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
@@ -1068,7 +1070,7 @@
int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 30, 30});
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, RR_11, RR_11);
mFrameTimeline->setSfPresent(26, presentFence1);
presentFence1->signalForTest(31);
@@ -1150,7 +1152,7 @@
addEmptySurfaceFrame();
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, RR_11, RR_11);
mFrameTimeline->setSfPresent(26, presentFence1);
presentFence1->signalForTest(31);
@@ -1252,7 +1254,7 @@
auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1398,7 +1400,7 @@
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(sfEndTime, presentFence1);
@@ -1475,7 +1477,7 @@
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, RR_11, RR_11);
surfaceFrame1->setDropTime(sfStartTime);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1528,7 +1530,7 @@
mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
- mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_11);
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(26, presentFence1);
@@ -1552,11 +1554,11 @@
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
- Fps vsyncRate = Fps::fromPeriodNsecs(11);
+ Fps vsyncRate = RR_11;
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
- mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate, vsyncRate);
mFrameTimeline->setSfPresent(26, presentFence1);
auto displayFrame = getDisplayFrame(0);
presentFence1->signalForTest(30);
@@ -1566,7 +1568,7 @@
// Trigger a flush by finalizing the next DisplayFrame
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate, vsyncRate);
mFrameTimeline->setSfPresent(56, presentFence2);
displayFrame = getDisplayFrame(0);
@@ -1591,11 +1593,11 @@
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
- Fps vsyncRate = Fps::fromPeriodNsecs(11);
+ Fps vsyncRate = RR_11;
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
- mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate, vsyncRate);
mFrameTimeline->setSfPresent(26, presentFence1);
auto displayFrame = getDisplayFrame(0);
presentFence1->signalForTest(50);
@@ -1605,7 +1607,7 @@
// Trigger a flush by finalizing the next DisplayFrame
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate, vsyncRate);
mFrameTimeline->setSfPresent(56, presentFence2);
displayFrame = getDisplayFrame(0);
@@ -1633,7 +1635,7 @@
TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) {
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({12, 18, 40});
- mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, RR_11, RR_11);
mFrameTimeline->setSfPresent(22, presentFence1);
auto displayFrame = getDisplayFrame(0);
@@ -1673,7 +1675,7 @@
int64_t sfToken4 = mTokenManager->generateTokenForPredictions({112, 120, 120});
// case 1 - cpu time = 33 - 12 = 21, vsync period = 11
- mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, RR_11, RR_11);
mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
auto displayFrame0 = getDisplayFrame(0);
gpuFence1->signalForTest(36);
@@ -1683,7 +1685,7 @@
EXPECT_EQ(displayFrame0->getActuals().presentTime, 0);
// case 2 - cpu time = 56 - 52 = 4, vsync period = 30
- mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, RR_30, RR_30);
mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
auto displayFrame1 = getDisplayFrame(1);
gpuFence2->signalForTest(76);
@@ -1697,7 +1699,7 @@
EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
// case 3 - cpu time = 86 - 82 = 4, vsync period = 30
- mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken3, 106, RR_30, RR_30);
mFrameTimeline->setSfPresent(112, presentFence3, gpuFence3);
auto displayFrame2 = getDisplayFrame(2);
gpuFence3->signalForTest(116);
@@ -1711,7 +1713,7 @@
EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
// case 4 - cpu time = 86 - 82 = 4, vsync period = 30
- mFrameTimeline->setSfWakeUp(sfToken4, 120, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken4, 120, RR_30, RR_30);
mFrameTimeline->setSfPresent(140, presentFence4, gpuFence4);
auto displayFrame3 = getDisplayFrame(3);
gpuFence4->signalForTest(156);
@@ -1748,7 +1750,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(16);
- mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(27, presentFence1);
@@ -1771,7 +1773,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(36);
- mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, RR_11, RR_11);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
mFrameTimeline->setSfPresent(57, presentFence2);
@@ -1799,10 +1801,10 @@
::testing::Mock::VerifyAndClearExpectations(mTimeStats.get());
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
- sLayerNameOne, sGameMode,
- JankType::PredictionError, -3, 5, 0}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{RR_11, std::nullopt, sUidOne,
+ sLayerNameOne, sGameMode,
+ JankType::PredictionError, -3, 5,
+ 0}));
addEmptyDisplayFrame();
@@ -1834,7 +1836,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(16);
- mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(26, presentFence1);
@@ -1857,7 +1859,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(36);
- mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, RR_11, RR_11);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
mFrameTimeline->setSfPresent(57, presentFence2);
@@ -1885,10 +1887,10 @@
::testing::Mock::VerifyAndClearExpectations(mTimeStats.get());
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
- sLayerNameOne, sGameMode,
- JankType::PredictionError, -3, 5, 0}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{RR_11, std::nullopt, sUidOne,
+ sLayerNameOne, sGameMode,
+ JankType::PredictionError, -3, 5,
+ 0}));
addEmptyDisplayFrame();
@@ -1919,7 +1921,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(40);
- mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 42, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(46, presentFence1);
@@ -1966,7 +1968,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(26);
- mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 32, RR_11, RR_11);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(36, presentFence1);
@@ -1989,7 +1991,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(40);
- mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken2, 43, RR_11, RR_11);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
mFrameTimeline->setSfPresent(56, presentFence2);
@@ -2047,7 +2049,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(50);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, RR_30, RR_30);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(56, presentFence1);
@@ -2070,7 +2072,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(84);
- mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken2, 112, RR_30, RR_30);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
mFrameTimeline->setSfPresent(116, presentFence2);
@@ -2131,7 +2133,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(50);
- mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, RR_30, RR_30);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(56, presentFence1);
@@ -2154,7 +2156,7 @@
sLayerNameOne, sLayerNameOne,
/*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(80);
- mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken2, 82, RR_30, RR_30);
// Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2206,7 +2208,7 @@
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
// Case 1: cpu time = 33 - 12 = 21, vsync period = 11
- mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, RR_11, RR_11);
mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
auto displayFrame = getDisplayFrame(0);
gpuFence1->signalForTest(36);
@@ -2225,7 +2227,7 @@
EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
// Case 2: No GPU fence so it will not use GPU composition.
- mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, RR_30, RR_30);
mFrameTimeline->setSfPresent(66, presentFence2);
auto displayFrame2 = getDisplayFrame(2); // 2 because of previous empty frame
presentFence2->signalForTest(90);
@@ -2250,13 +2252,13 @@
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
int64_t sfToken3 = mTokenManager->generateTokenForPredictions({72, 80, 80});
- mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_11);
mFrameTimeline->setSfPresent(26, erroneousPresentFence1);
- mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, RR_11, RR_11);
mFrameTimeline->setSfPresent(60, erroneousPresentFence2);
- mFrameTimeline->setSfWakeUp(sfToken3, 72, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfWakeUp(sfToken3, 72, RR_11, RR_11);
mFrameTimeline->setSfPresent(80, validPresentFence);
erroneousPresentFence2->signalForTest(2);
@@ -2479,4 +2481,44 @@
mFrameTimeline->setSfPresent(50, presentFence);
ASSERT_EQ(surfaceFrame->getBaseTime(), 50);
}
+
+TEST_F(FrameTimelineTest, surfaceFrameRenderRateUsingDisplayRate) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ FrameTimelineInfo ftInfo;
+ ftInfo.vsyncId = token1;
+ ftInfo.inputEventId = sInputEventId;
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+
+ mFrameTimeline->setSfWakeUp(token1, 20, RR_30, RR_11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ presentFence1->signalForTest(std::chrono::nanoseconds(50ns).count());
+ mFrameTimeline->setSfPresent(50, presentFence1);
+
+ EXPECT_EQ(surfaceFrame->getRenderRate().getPeriodNsecs(), 11);
+}
+
+TEST_F(FrameTimelineTest, surfaceFrameRenderRateUsingAppFrameRate) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ FrameTimelineInfo ftInfo;
+ ftInfo.vsyncId = token1;
+ ftInfo.inputEventId = sInputEventId;
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+ surfaceFrame->setRenderRate(RR_30);
+ mFrameTimeline->setSfWakeUp(token1, 20, RR_11, RR_11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ presentFence1->signalForTest(std::chrono::nanoseconds(50ns).count());
+ mFrameTimeline->setSfPresent(50, presentFence1);
+
+ EXPECT_EQ(surfaceFrame->getRenderRate().getPeriodNsecs(), 30);
+}
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index d319dcc..48f8923 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -421,6 +421,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setDamageRegion(uint32_t id, const Region& damageRegion) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setDataspace(uint32_t id, ui::Dataspace dataspace) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -432,6 +443,19 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
+ layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.matrix = matrix;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setShadowRadius(uint32_t id, float shadowRadius) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -443,6 +467,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setTrustedOverlay(uint32_t id, bool isTrustedOverlay) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.isTrustedOverlay = isTrustedOverlay;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index a462082..631adf1 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -866,6 +866,104 @@
}
}
+class SmallAreaDetectionTest : public LayerHistoryIntegrationTest {
+protected:
+ static constexpr int32_t DISPLAY_WIDTH = 100;
+ static constexpr int32_t DISPLAY_HEIGHT = 100;
+
+ static constexpr int32_t kAppId1 = 10100;
+ static constexpr int32_t kAppId2 = 10101;
+
+ static constexpr float kThreshold1 = 0.05f;
+ static constexpr float kThreshold2 = 0.07f;
+
+ SmallAreaDetectionTest() : LayerHistoryIntegrationTest() {
+ std::vector<std::pair<int32_t, float>> mappings;
+ mappings.reserve(2);
+ mappings.push_back(std::make_pair(kAppId1, kThreshold1));
+ mappings.push_back(std::make_pair(kAppId2, kThreshold2));
+
+ mFlinger.enableNewFrontEnd();
+
+ mScheduler->onActiveDisplayAreaChanged(DISPLAY_WIDTH * DISPLAY_HEIGHT);
+ mScheduler->updateSmallAreaDetection(mappings);
+ }
+
+ auto createLegacyAndFrontedEndLayer(uint32_t sequence) {
+ std::string layerName = "test layer:" + std::to_string(sequence);
+
+ LayerCreationArgs args = LayerCreationArgs{mFlinger.flinger(),
+ nullptr,
+ layerName,
+ 0,
+ {},
+ std::make_optional<uint32_t>(sequence)};
+ args.ownerUid = kAppId1;
+ args.metadata.setInt32(gui::METADATA_WINDOW_TYPE, 2); // APPLICATION
+ const auto layer = sp<Layer>::make(args);
+ mFlinger.injectLegacyLayer(layer);
+ createRootLayer(sequence);
+ return layer;
+ }
+};
+
+TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ setDamageRegion(sequence, Region(Rect(10, 10)));
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ setDamageRegion(sequence, Region(Rect(50, 50)));
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, smallDirtyLayerWithMatrix) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // Original damage region is a small dirty.
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ setDamageRegion(sequence, Region(Rect(20, 20)));
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+
+ setMatrix(sequence, 2.0f, 0, 0, 2.0f);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ // Verify if the small dirty is scaled.
+ ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryIntegrationTestParameterized,
::testing::Values(1s, 2s, 3s, 4s, 5s));
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 1fde9b8..aecfcba 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -525,4 +525,39 @@
mLifecycleManager.commitChanges();
}
+// Even when it does not change visible region, we should mark alpha changes as affecting
+// visible region because HWC impl depends on it. writeOutputIndependentGeometryStateToHWC
+// is only called if we are updating geometry.
+TEST_F(LayerLifecycleManagerTest, alphaChangesAlwaysSetsVisibleRegionFlag) {
+ mLifecycleManager.commitChanges();
+ float startingAlpha = 0.5f;
+ setAlpha(1, startingAlpha);
+
+ // this is expected because layer alpha changes from 1 to 0.5, it may no longer be opaque
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges().string(),
+ ftl::Flags<RequestedLayerState::Changes>(
+ RequestedLayerState::Changes::Content |
+ RequestedLayerState::Changes::AffectsChildren |
+ RequestedLayerState::Changes::VisibleRegion)
+ .string());
+ EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(startingAlpha));
+ mLifecycleManager.commitChanges();
+
+ float endingAlpha = 0.2f;
+ setAlpha(1, endingAlpha);
+
+ // this is not expected but we should make sure this behavior does not change
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges().string(),
+ ftl::Flags<RequestedLayerState::Changes>(
+ RequestedLayerState::Changes::Content |
+ RequestedLayerState::Changes::AffectsChildren |
+ RequestedLayerState::Changes::VisibleRegion)
+ .string());
+ EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(endingAlpha));
+ mLifecycleManager.commitChanges();
+
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges().string(),
+ ftl::Flags<RequestedLayerState::Changes>().string());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 50dfcaa..e784eb7 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -282,17 +282,7 @@
// │ └── 13
// └── 2
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
- transactions.back().states.front().state.frameRate = 90.0;
- transactions.back().states.front().state.frameRateCompatibility =
- ANATIVEWINDOW_FRAME_RATE_EXACT;
- transactions.back().states.front().state.changeFrameRateStrategy =
- ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS;
- transactions.back().states.front().layerId = 11;
- mLifecycleManager.applyTransactions(transactions);
+ setFrameRate(11, 90.0, ANATIVEWINDOW_FRAME_RATE_EXACT, ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(11)->frameRate.vote.rate.getIntValue(), 90);
@@ -303,6 +293,42 @@
EXPECT_EQ(getSnapshot(1)->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote);
}
+TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotesDoesNotAffectSiblings) {
+ // ROOT
+ // ├── 1 (verify layer has no vote)
+ // │ ├── 11 (frame rate set)
+ // │ │ └── 111
+ // │ ├── 12 (frame rate set)
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13 (verify layer has default vote)
+ // └── 2
+
+ setFrameRate(11, 90.0, ANATIVEWINDOW_FRAME_RATE_EXACT, ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS);
+ setFrameRate(12, 45.0, ANATIVEWINDOW_FRAME_RATE_EXACT, ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(12)->frameRate.vote.rate.getIntValue(), 45);
+ EXPECT_EQ(getSnapshot(12)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(121)->frameRate.vote.rate.getIntValue(), 45);
+ EXPECT_EQ(getSnapshot(121)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(1221)->frameRate.vote.rate.getIntValue(), 45);
+ EXPECT_EQ(getSnapshot(1221)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot(13)->frameRate.vote.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(13)->frameRate.vote.type, scheduler::FrameRateCompatibility::Default);
+ EXPECT_EQ(getSnapshot(2)->frameRate.vote.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(2)->frameRate.vote.type, scheduler::FrameRateCompatibility::Default);
+}
+
TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
// ROOT
// ├── 1
@@ -861,4 +887,15 @@
EXPECT_EQ(getSnapshot(1)->shadowSettings.length, SHADOW_RADIUS);
}
+TEST_F(LayerSnapshotTest, setTrustedOverlayForNonVisibleInput) {
+ hideLayer(1);
+ setTrustedOverlay(1, true);
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegion(1, touch);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 0b67137..faa12a1 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -117,9 +117,9 @@
return std::make_pair(ranking, consideredSignals);
}
- ftl::NonNull<DisplayModePtr> getBestFrameRateMode(
- const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const {
- return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
+ FrameRateMode getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
+ GlobalSignals signals = {}) const {
+ return getRankedFrameRates(layers, signals).ranking.front().frameRateMode;
}
ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {},
@@ -429,11 +429,11 @@
// If there are no layers we select the default frame rate, which is the max of the primary
// range.
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode());
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode().modePtr);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode());
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode().modePtr);
}
{
// We select max even when this will cause a non-seamless switch.
@@ -442,7 +442,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy(
{kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching}));
- EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode());
+ EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode().modePtr);
}
}
@@ -455,7 +455,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) {
@@ -466,107 +466,107 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.name = "";
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) {
@@ -577,32 +577,32 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) {
@@ -612,26 +612,26 @@
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) {
@@ -645,19 +645,19 @@
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) {
@@ -673,7 +673,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -681,7 +681,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -689,7 +689,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -697,7 +697,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -705,7 +705,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -713,7 +713,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
@@ -721,7 +721,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -729,7 +729,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -737,7 +737,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest,
@@ -756,7 +756,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -764,7 +764,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -772,7 +772,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -780,7 +780,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -788,7 +788,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -796,7 +796,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
@@ -804,7 +804,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -812,7 +812,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -820,14 +820,14 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -835,7 +835,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "120Hz ExplicitDefault";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -843,7 +843,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::ExplicitExact;
lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 10_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -851,7 +851,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
lr1.desiredRefreshRate = 30_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -862,7 +862,7 @@
lr3.vote = LayerVoteType::Heuristic;
lr3.desiredRefreshRate = 120_Hz;
lr3.name = "120Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) {
@@ -872,26 +872,26 @@
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) {
@@ -902,42 +902,42 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers).modePtr);
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
lr.desiredRefreshRate = 24_Hz;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers).modePtr);
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) {
@@ -949,39 +949,39 @@
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 15_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 30_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) {
@@ -993,7 +993,7 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestFrameRateMode(layers);
+ const auto mode = selector.getBestFrameRateMode(layers).modePtr;
EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getPeakFps()) << "("
<< to_string(mode->getVsyncRate()) << ")";
@@ -1009,7 +1009,7 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestFrameRateMode(layers);
+ const auto mode = selector.getBestFrameRateMode(layers).modePtr;
EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getPeakFps()) << "("
<< to_string(mode->getVsyncRate()) << ")";
@@ -1027,19 +1027,19 @@
lr1.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) {
@@ -1051,7 +1051,7 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestFrameRateMode(layers, {});
+ const auto mode = selector.getBestFrameRateMode(layers, {}).modePtr;
EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getPeakFps()) << "("
<< to_string(mode->getVsyncRate()) << ")";
@@ -1071,7 +1071,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
@@ -1079,14 +1079,14 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30_Hz;
@@ -1094,14 +1094,14 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30_Hz;
lr1.name = "30Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
@@ -1116,28 +1116,28 @@
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1146,7 +1146,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
@@ -1464,7 +1464,8 @@
layers.push_back(layer);
}
- EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getPeakFps())
+ EXPECT_EQ(testCase.expectedFrameRate,
+ selector.getBestFrameRateMode(layers).modePtr->getPeakFps())
<< "Did not get expected frame rate for frameRate="
<< to_string(testCase.desiredFrameRate)
<< " category=" << ftl::enum_string(testCase.frameRateCategory);
@@ -1528,13 +1529,147 @@
layers.push_back(layer);
}
- EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getPeakFps())
+ EXPECT_EQ(testCase.expectedFrameRate,
+ selector.getBestFrameRateMode(layers).modePtr->getPeakFps())
<< "Did not get expected frame rate for frameRate="
<< to_string(testCase.desiredFrameRate)
<< " category=" << ftl::enum_string(testCase.frameRateCategory);
}
}
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withFrameRateCategory_smoothSwitchOnly_60_120_nonVrr) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ // VRR compatibility is determined by the presence of a vrr config in the DisplayMode.
+ auto selector = createSelector(makeModes(kMode60, kMode120), kModeId120);
+
+ struct Case {
+ // Params
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+ bool smoothSwitchOnly = false;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ DisplayModeId expectedModeId = kModeId60;
+ };
+
+ const std::initializer_list<Case> testCases = {
+ // These layers may switch modes because smoothSwitchOnly=false.
+ {FrameRateCategory::Default, false, 120_Hz, kModeId120},
+ // TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
+ {FrameRateCategory::NoPreference, false, 60_Hz, kModeId60},
+ {FrameRateCategory::Low, false, 30_Hz, kModeId60},
+ {FrameRateCategory::Normal, false, 60_Hz, kModeId60},
+ {FrameRateCategory::High, false, 120_Hz, kModeId120},
+
+ // These layers cannot change mode due to smoothSwitchOnly, and will definitely use
+ // active mode (120Hz).
+ {FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Normal, true, 40_Hz, kModeId120},
+ {FrameRateCategory::High, true, 120_Hz, kModeId120},
+ };
+
+ for (auto testCase : testCases) {
+ std::vector<LayerRequirement> layers;
+ ALOGI("**** %s: Testing frameRateCategory=%s (smooth=%d)", __func__,
+ ftl::enum_string(testCase.frameRateCategory).c_str(), testCase.smoothSwitchOnly);
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory)
+ << " smooth:" << testCase.smoothSwitchOnly << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .frameRateCategorySmoothSwitchOnly =
+ testCase.smoothSwitchOnly,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ auto actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ EXPECT_EQ(testCase.expectedFrameRate, actualFrameRateMode.fps)
+ << "Did not get expected frame rate for category="
+ << ftl::enum_string(testCase.frameRateCategory)
+ << " (smooth=" << testCase.smoothSwitchOnly << ")";
+
+ EXPECT_EQ(testCase.expectedModeId, actualFrameRateMode.modePtr->getId())
+ << "Did not get expected mode for category="
+ << ftl::enum_string(testCase.frameRateCategory)
+ << " (smooth=" << testCase.smoothSwitchOnly << ")";
+ }
+}
+
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withFrameRateCategory_smoothSwitchOnly_60_120_vrr) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ // VRR compatibility is determined by the presence of a vrr config in the DisplayMode.
+ auto selector = createSelector(kVrrModes_60_120, kModeId120);
+
+ struct Case {
+ // Params
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+ bool smoothSwitchOnly = false;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ // Note that `smoothSwitchOnly` should not have an effect.
+ const std::initializer_list<Case> testCases = {
+ {FrameRateCategory::Default, false, 240_Hz},
+ // TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
+ {FrameRateCategory::NoPreference, false, 240_Hz},
+ {FrameRateCategory::Low, false, 30_Hz},
+ {FrameRateCategory::Normal, false, 60_Hz},
+ {FrameRateCategory::High, false, 120_Hz},
+ {FrameRateCategory::Default, true, 240_Hz},
+ // TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
+ {FrameRateCategory::NoPreference, true, 240_Hz},
+ {FrameRateCategory::Low, true, 30_Hz},
+ {FrameRateCategory::Normal, true, 60_Hz},
+ {FrameRateCategory::High, true, 120_Hz},
+ };
+
+ for (auto testCase : testCases) {
+ std::vector<LayerRequirement> layers;
+ ALOGI("**** %s: Testing frameRateCategory=%s (smooth=%d)", __func__,
+ ftl::enum_string(testCase.frameRateCategory).c_str(), testCase.smoothSwitchOnly);
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory)
+ << " smooth:" << testCase.smoothSwitchOnly << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .frameRateCategorySmoothSwitchOnly =
+ testCase.smoothSwitchOnly,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ auto actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ EXPECT_EQ(testCase.expectedFrameRate, actualFrameRateMode.fps)
+ << "Did not get expected frame rate for category="
+ << ftl::enum_string(testCase.frameRateCategory)
+ << " (smooth=" << testCase.smoothSwitchOnly << ")";
+
+ // Expect all cases to be able to stay at the mode with TE 240 due to VRR compatibility.
+ EXPECT_EQ(kVrrMode120TE240->getId(), actualFrameRateMode.modePtr->getId())
+ << "Did not get expected mode for category="
+ << ftl::enum_string(testCase.frameRateCategory)
+ << " (smooth=" << testCase.smoothSwitchOnly << ")";
+ }
+}
+
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
auto selector = createSelector(kModes_60_90_72_120, kModeId60);
@@ -1568,7 +1703,7 @@
ss << "ExplicitDefault " << desired;
lr.name = ss.str();
- const auto bestMode = selector.getBestFrameRateMode(layers);
+ const auto bestMode = selector.getBestFrameRateMode(layers).modePtr;
EXPECT_EQ(expected, bestMode->getPeakFps())
<< "expected " << expected << " for " << desired << " but got "
<< bestMode->getPeakFps() << "(" << bestMode->getVsyncRate() << ")";
@@ -1589,7 +1724,7 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 23.976_Hz;
lr.name = "ExplicitExactOrMultiple 23.976 Hz";
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
}
// Test that 24 will choose 23.976 if 24 is not supported
@@ -1600,7 +1735,7 @@
lr.desiredRefreshRate = 24_Hz;
lr.name = "ExplicitExactOrMultiple 24 Hz";
- EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers).modePtr->getId());
}
// Test that 29.97 will prefer 59.94 over 60 and 30
@@ -1611,7 +1746,7 @@
lr.desiredRefreshRate = 29.97_Hz;
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers).modePtr->getId());
}
// Test that 29.97 will choose 60 if 59.94 is not supported
@@ -1620,7 +1755,7 @@
lr.desiredRefreshRate = 29.97_Hz;
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
}
// Test that 59.94 will choose 60 if 59.94 is not supported
@@ -1629,7 +1764,7 @@
lr.desiredRefreshRate = 59.94_Hz;
lr.name = "ExplicitExactOrMultiple 59.94 Hz";
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
}
}
@@ -1648,7 +1783,8 @@
ss << "ExplicitExact " << desired;
lr.name = ss.str();
- EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getPeakFps());
+ EXPECT_EQ(lr.desiredRefreshRate,
+ selector.getBestFrameRateMode(layers).modePtr->getPeakFps());
}
}
}
@@ -1694,7 +1830,7 @@
lr.desiredRefreshRate = 90_Hz;
lr.name = "90Hz ExplicitDefault";
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true}).modePtr);
}
TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) {
@@ -1905,46 +2041,46 @@
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitExactOrMultiple";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::ExplicitDefault;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitDefault";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.focused = true;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Heuristic;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Max;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Max";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.vote = LayerVoteType::Min;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Min";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
}
TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
@@ -1960,7 +2096,7 @@
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
@@ -1978,7 +2114,7 @@
layer.seamlessness = Seamlessness::SeamedAndSeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
@@ -1997,7 +2133,7 @@
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
@@ -2018,7 +2154,7 @@
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
@@ -2042,7 +2178,7 @@
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
@@ -2071,7 +2207,7 @@
layers[1].name = "90Hz ExplicitDefault";
layers[1].focused = false;
- EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
@@ -2104,7 +2240,7 @@
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
@@ -2134,7 +2270,7 @@
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
@@ -2154,10 +2290,10 @@
layer.name = "60Hz ExplicitExactOrMultiple";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers).modePtr->getId());
selector.setActiveMode(kModeId120, 120_Hz);
- EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
@@ -2182,14 +2318,14 @@
.weight = 1.f,
.focused = true}};
- EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers).modePtr->getId());
auto& seamedLayer = layers[0];
seamedLayer.desiredRefreshRate = 30_Hz;
seamedLayer.name = "30Hz ExplicitDefault";
selector.setActiveMode(kModeId30, 30_Hz);
- EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
@@ -2204,7 +2340,7 @@
std::vector<LayerRequirement> layers = {
{.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
- EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
@@ -2225,7 +2361,7 @@
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
layers[0].focused = args.focused;
- return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId();
+ return selector.getBestFrameRateMode(layers, {.touch = args.touch}).modePtr->getId();
};
constexpr FpsRange k30_60 = {30_Hz, 60_Hz};
@@ -2234,7 +2370,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}}));
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode().modePtr->getId());
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
@@ -2302,7 +2438,8 @@
}
// With no layers, idle should still be lower priority than touch boost.
- EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+ EXPECT_EQ(kModeId90,
+ selector.getBestFrameRateMode({}, {.touch = true, .idle = true}).modePtr->getId());
// Idle should be higher precedence than other layer frame rate considerations.
selector.setActiveMode(kModeId90, 90_Hz);
@@ -2319,7 +2456,7 @@
}
// Idle should be applied rather than the active mode when there are no layers.
- EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true}).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) {
@@ -2368,7 +2505,7 @@
for (const auto& [fps, mode] : knownFrameRatesExpectations) {
layer.desiredRefreshRate = fps;
- EXPECT_EQ(mode, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(mode, selector.getBestFrameRateMode(layers).modePtr);
}
}
@@ -2471,17 +2608,17 @@
explicitExactLayer.name = "ExplicitExact";
explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
if (GetParam() == Config::FrameRateOverride::Disabled) {
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
} else {
- EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
}
explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}).modePtr);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) {
@@ -2499,7 +2636,7 @@
explicitDefaultLayer.name = "ExplicitDefault";
explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
- EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
}
// b/190578904
@@ -2526,7 +2663,7 @@
layers[0].desiredRefreshRate = fps;
layers[0].vote = vote;
EXPECT_EQ(fps.getIntValue(),
- selector.getBestFrameRateMode(layers)->getPeakFps().getIntValue())
+ selector.getBestFrameRateMode(layers).modePtr->getPeakFps().getIntValue())
<< "Failed for " << ftl::enum_string(vote);
};
@@ -2565,7 +2702,7 @@
},
};
- EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getPeakFps());
+ EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals).modePtr->getPeakFps());
}
TEST_P(RefreshRateSelectorTest, modeComparison) {
@@ -3176,7 +3313,8 @@
selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}}));
// With no layers, idle should still be lower priority than touch boost.
- EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+ EXPECT_EQ(kModeId90,
+ selector.getBestFrameRateMode({}, {.touch = true, .idle = true}).modePtr->getId());
// Idle should be higher precedence than other layer frame rate considerations.
selector.setActiveMode(kModeId90, 90_Hz);
@@ -3192,7 +3330,7 @@
}
// Idle should be applied rather than the active mode when there are no layers.
- EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+ EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true}).modePtr->getId());
}
TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) {
diff --git a/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp b/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
index b910485..05f9eed 100644
--- a/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
@@ -23,38 +23,34 @@
namespace android::scheduler {
-class SmallAreaDetectionMappingsAllowTest : public testing::Test {
+class SmallAreaDetectionAllowMappingsTest : public testing::Test {
protected:
SmallAreaDetectionAllowMappings mMappings;
+ static constexpr int32_t kAppId1 = 10100;
+ static constexpr int32_t kAppId2 = 10101;
+ static constexpr float kThreshold1 = 0.05f;
+ static constexpr float kThreshold2 = 0.07f;
};
namespace {
-TEST_F(SmallAreaDetectionMappingsAllowTest, testUpdate) {
- const uid_t uid1 = 10100;
- const uid_t uid2 = 10101;
- const float threshold1 = 0.05f;
- const float threshold2 = 0.07f;
- std::vector<std::pair<uid_t, float>> mappings;
+TEST_F(SmallAreaDetectionAllowMappingsTest, testUpdate) {
+ std::vector<std::pair<int32_t, float>> mappings;
mappings.reserve(2);
- mappings.push_back(std::make_pair(uid1, threshold1));
- mappings.push_back(std::make_pair(uid2, threshold2));
+ mappings.push_back(std::make_pair(kAppId1, kThreshold1));
+ mappings.push_back(std::make_pair(kAppId2, kThreshold2));
mMappings.update(mappings);
- ASSERT_EQ(mMappings.getThresholdForUid(uid1).value(), threshold1);
- ASSERT_EQ(mMappings.getThresholdForUid(uid2).value(), threshold2);
+ ASSERT_EQ(mMappings.getThresholdForAppId(kAppId1).value(), kThreshold1);
+ ASSERT_EQ(mMappings.getThresholdForAppId(kAppId2).value(), kThreshold2);
}
-TEST_F(SmallAreaDetectionMappingsAllowTest, testSetThesholdForUid) {
- const uid_t uid = 10111;
- const float threshold = 0.05f;
-
- mMappings.setThesholdForUid(uid, threshold);
- ASSERT_EQ(mMappings.getThresholdForUid(uid), threshold);
+TEST_F(SmallAreaDetectionAllowMappingsTest, testSetThesholdForAppId) {
+ mMappings.setThesholdForAppId(kAppId1, kThreshold1);
+ ASSERT_EQ(mMappings.getThresholdForAppId(kAppId1), kThreshold1);
}
-TEST_F(SmallAreaDetectionMappingsAllowTest, testUidNotInTheMappings) {
- const uid_t uid = 10222;
- ASSERT_EQ(mMappings.getThresholdForUid(uid), std::nullopt);
+TEST_F(SmallAreaDetectionAllowMappingsTest, testAppIdNotInTheMappings) {
+ ASSERT_EQ(mMappings.getThresholdForAppId(kAppId1), std::nullopt);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 014d07c..3d1c900 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,7 +44,10 @@
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
- : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+ : Scheduler(*this, callback,
+ (FeatureFlags)Feature::kContentDetection |
+ Feature::kSmallDirtyContentDetection,
+ std::move(modulatorPtr)) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
std::move(tracker));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b54392e..8f1982d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -611,6 +611,15 @@
void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
+ auto setDaltonizerType(ColorBlindnessType type) {
+ mFlinger->mDaltonizer.setType(type);
+ return mFlinger->updateColorMatrixLocked();
+ }
+ auto updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
+ bool& out) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ return mFlinger->updateLayerSnapshots(vsyncId, frameTimeNs, transactionsFlushed, out);
+ }
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -661,6 +670,11 @@
return mFlinger->initTransactionTraceWriter();
}
+ void enableNewFrontEnd() {
+ mFlinger->mLayerLifecycleManagerEnabled = true;
+ mFlinger->mLegacyFrontEndEnabled = false;
+ }
+
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the SurfaceFlinger instance may
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index caa265f..5046a86 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -347,7 +347,7 @@
// Both the droppedSurfaceFrame and presentedSurfaceFrame should be in
// pendingJankClassifications.
EXPECT_EQ(2u, layer->mPendingJankClassifications.size());
- presentedSurfaceFrame->onPresent(20, JankType::None, 90_Hz,
+ presentedSurfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz,
/*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
layer->releasePendingBuffer(25);
@@ -484,10 +484,10 @@
// BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
// Since we don't have access to DisplayFrame here, trigger an onPresent directly.
for (auto& surfaceFrame : bufferlessSurfaceFrames) {
- surfaceFrame->onPresent(20, JankType::None, 90_Hz,
+ surfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz,
/*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
}
- presentedBufferSurfaceFrame->onPresent(20, JankType::None, 90_Hz,
+ presentedBufferSurfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz,
/*displayDeadlineDelta*/ 0,
/*displayPresentDelta*/ 0);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index a1eda94..1dc5498 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -759,6 +759,8 @@
// b/1450138150
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
auto result =
@@ -772,6 +774,29 @@
{.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1200, *result);
+
+ advanceToNextCallback();
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+// b/1450138150
+TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+
+ EXPECT_CALL(mMockClock, alarmAt(_, 500));
+ CountingCallback cb(mDispatch);
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
+ mMockClock.advanceBy(400);
+
+ result = mDispatch->schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(400, *result);
+
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
}
@@ -826,6 +851,8 @@
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+
EXPECT_CALL(mMockClock, alarmAt(_, 600));
CountingCallback cb(mDispatch);
@@ -843,6 +870,26 @@
advanceToNextCallback();
}
+TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+
+ EXPECT_CALL(mMockClock, alarmAt(_, 600));
+
+ CountingCallback cb(mDispatch);
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+
+ result = mDispatch->schedule(cb,
+ {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(0, *result);
+
+ advanceToNextCallback();
+}
+
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
@@ -1045,6 +1092,8 @@
}
TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+
Sequence seq;
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
@@ -1065,10 +1114,31 @@
EXPECT_THAT(cb.mReadyTime[0], Eq(2000));
}
-// TODO(b/304338314): Set the flag value instead of skipping the test
+TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+
+ CountingCallback cb(mDispatch);
+
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+
+ advanceToNextCallback();
+
+ advanceToNextCallback();
+
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb.mCalls[0], Eq(1000));
+ ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb.mWakeupTime[0], Eq(600));
+ ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb.mReadyTime[0], Eq(1000));
+}
+
TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) {
- // SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
- if (flags::dont_skip_on_early()) GTEST_SKIP();
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
@@ -1088,10 +1158,8 @@
ASSERT_THAT(cb.mCalls.size(), Eq(1));
}
-// TODO(b/304338314): Set the flag value instead of skipping the test
TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) {
- // SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
- if (!flags::dont_skip_on_early()) GTEST_SKIP();
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
@@ -1105,7 +1173,7 @@
result = mDispatch->schedule(cb,
{.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(200, *result);
+ EXPECT_EQ(300, *result);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 5dc48c3..3f2a917 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -31,7 +31,7 @@
MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
- MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
+ MOCK_METHOD4(setSfWakeUp, void(int64_t, nsecs_t, Fps, Fps));
MOCK_METHOD3(setSfPresent,
void(nsecs_t, const std::shared_ptr<FenceTime>&,
const std::shared_ptr<FenceTime>&));