Merge "Polish up metadata propagation for native window" into tm-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 24b201f..890c15f 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2733,8 +2733,8 @@
const android::base::unique_fd& screenshot_fd_in,
bool is_screenshot_requested) {
// Duplicate the fds because the passed in fds don't outlive the binder transaction.
- bugreport_fd.reset(dup(bugreport_fd_in.get()));
- screenshot_fd.reset(dup(screenshot_fd_in.get()));
+ bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
+ screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested);
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index c104fea..4e12579 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1176,6 +1176,25 @@
return res;
}
+binder::Status InstalldNativeService::deleteReferenceProfile(const std::string& packageName,
+ const std::string& profileName) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ LOCK_PACKAGE();
+
+ // This function only supports primary dex'es.
+ std::string path =
+ create_reference_profile_path(packageName, profileName, /*is_secondary_dex=*/false);
+ if (unlink(path.c_str()) != 0) {
+ if (errno == ENOENT) {
+ return ok();
+ } else {
+ return error("Failed to delete profile " + profileName + " for " + packageName);
+ }
+ }
+ return ok();
+}
+
binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
ENFORCE_UID(AID_SYSTEM);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 87a9206..0432222 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -140,6 +140,8 @@
bool* _aidl_return);
binder::Status clearAppProfiles(const std::string& packageName, const std::string& profileName);
binder::Status destroyAppProfiles(const std::string& packageName);
+ binder::Status deleteReferenceProfile(const std::string& packageName,
+ const std::string& profileName);
binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName,
const std::string& profileName, const std::string& classpath, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 79c02e8..db03411 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -82,6 +82,7 @@
@utf8InCpp String packageName, @utf8InCpp String profileName);
void clearAppProfiles(@utf8InCpp String packageName, @utf8InCpp String profileName);
void destroyAppProfiles(@utf8InCpp String packageName);
+ void deleteReferenceProfile(@utf8InCpp String packageName, @utf8InCpp String profileName);
boolean createProfileSnapshot(int appId, @utf8InCpp String packageName,
@utf8InCpp String profileName, @utf8InCpp String classpath);
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 9801a9b..45aeab6 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -707,16 +707,16 @@
auto temp_dir_path =
base::StringPrintf("%s/%s", Dirname(pathname).c_str(), temp_dir_name.c_str());
- if (::rename(pathname.c_str(), temp_dir_path.c_str())) {
+ auto dir_to_delete = temp_dir_path.c_str();
+ if (::rename(pathname.c_str(), dir_to_delete)) {
if (ignore_if_missing && (errno == ENOENT)) {
return 0;
}
- ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), temp_dir_path.c_str(),
- strerror(errno));
- return -errno;
+ ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), dir_to_delete, strerror(errno));
+ dir_to_delete = pathname.c_str();
}
- return delete_dir_contents(temp_dir_path.c_str(), 1, exclusion_predicate, ignore_if_missing);
+ return delete_dir_contents(dir_to_delete, 1, exclusion_predicate, ignore_if_missing);
}
bool is_renamed_deleted_dir(const std::string& path) {
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 4c83a14..ee392fc 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -235,7 +235,7 @@
*
* Available since API level 33.
*/
-int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN(33);
+int android_tag_socket_with_uid(int sockfd, uint32_t tag, uid_t uid) __INTRODUCED_IN(33);
/*
* Set the socket tag for traffic statistics on the specified socket.
@@ -245,14 +245,26 @@
* opened by another UID or was previously tagged by another UID. Subsequent
* calls always replace any existing parameters. The socket tag is kept when the
* socket is sent to another process using binder IPCs or other mechanisms such
- * as UNIX socket fd passing.
+ * as UNIX socket fd passing. The tag is a value defined by the caller and used
+ * together with uid for data traffic accounting, so that the function callers
+ * can account different types of data usage for a uid.
*
* Returns 0 on success, or a negative POSIX error code (see errno.h) on
* failure.
*
+ * Some possible error codes:
+ * -EBADF Bad socketfd.
+ * -EPERM No permission.
+ * -EAFNOSUPPORT Socket family is neither AF_INET nor AF_INET6.
+ * -EPROTONOSUPPORT Socket protocol is neither IPPROTO_UDP nor IPPROTO_TCP.
+ * -EMFILE Too many stats entries.
+ * There are still other error codes that may provided by -errno of
+ * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by
+ * BPF maps read/write sys calls, which are set appropriately.
+ *
* Available since API level 33.
*/
-int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33);
+int android_tag_socket(int sockfd, uint32_t tag) __INTRODUCED_IN(33);
/*
* Untag a network socket.
@@ -267,6 +279,12 @@
* Returns 0 on success, or a negative POSIX error code (see errno.h) on
* failure.
*
+ * One of possible error code:
+ * -EBADF Bad socketfd.
+ * Other error codes are either provided by -errno of
+ * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by
+ * BPF map element deletion sys call, which are set appropriately.
+ *
* Available since API level 33.
*/
int android_untag_socket(int sockfd) __INTRODUCED_IN(33);
diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h
index 708eaf5..db3d9ea 100644
--- a/include/ftl/Flags.h
+++ b/include/ftl/Flags.h
@@ -19,13 +19,12 @@
#include <ftl/enum.h>
#include <ftl/string.h>
+#include <bitset>
#include <cstdint>
#include <iterator>
#include <string>
#include <type_traits>
-#include "utils/BitSet.h"
-
// TODO(b/185536303): Align with FTL style and namespace.
namespace android {
@@ -56,21 +55,22 @@
: mFlags(t) {}
class Iterator {
- // The type can't be larger than 64-bits otherwise it won't fit in BitSet64.
- static_assert(sizeof(U) <= sizeof(uint64_t));
+ using Bits = std::uint64_t;
+ static_assert(sizeof(U) <= sizeof(Bits));
public:
+ constexpr Iterator() = default;
Iterator(Flags<F> flags) : mRemainingFlags(flags.mFlags) { (*this)++; }
- Iterator() : mRemainingFlags(0), mCurrFlag(static_cast<F>(0)) {}
// Pre-fix ++
Iterator& operator++() {
- if (mRemainingFlags.isEmpty()) {
- mCurrFlag = static_cast<F>(0);
+ if (mRemainingFlags.none()) {
+ mCurrFlag = 0;
} else {
- uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left
- const U flag = 1 << (64 - bit - 1);
- mCurrFlag = static_cast<F>(flag);
+ // TODO: Replace with std::countr_zero in C++20.
+ const Bits bit = static_cast<Bits>(__builtin_ctzll(mRemainingFlags.to_ullong()));
+ mRemainingFlags.reset(static_cast<std::size_t>(bit));
+ mCurrFlag = static_cast<U>(static_cast<Bits>(1) << bit);
}
return *this;
}
@@ -88,7 +88,7 @@
bool operator!=(Iterator other) const { return !(*this == other); }
- F operator*() { return mCurrFlag; }
+ F operator*() const { return F{mCurrFlag}; }
// iterator traits
@@ -107,8 +107,8 @@
using pointer = void;
private:
- BitSet64 mRemainingFlags;
- F mCurrFlag;
+ std::bitset<sizeof(Bits) * 8> mRemainingFlags;
+ U mCurrFlag = 0;
};
/*
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
index 5234c05..82af1d6 100644
--- a/include/ftl/enum.h
+++ b/include/ftl/enum.h
@@ -261,10 +261,10 @@
const auto value = to_underlying(v);
// TODO: Replace with std::popcount and std::countr_zero in C++20.
- if (__builtin_popcountl(value) != 1) return {};
+ if (__builtin_popcountll(value) != 1) return {};
constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
- return kRange.values[__builtin_ctzl(value)];
+ return kRange.values[__builtin_ctzll(value)];
}
// Returns a stringified enumerator, or its integral value if not named.
diff --git a/include/ftl/fake_guard.h b/include/ftl/fake_guard.h
new file mode 100644
index 0000000..bacd1b2
--- /dev/null
+++ b/include/ftl/fake_guard.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#define FTL_ATTRIBUTE(a) __attribute__((a))
+
+namespace android::ftl {
+
+// Granular alternative to [[clang::no_thread_safety_analysis]]. Given a std::mutex-like object,
+// FakeGuard suppresses enforcement of thread-safe access to guarded variables within its scope.
+// While FakeGuard is scoped to a block, there are macro shorthands for a single expression, as
+// well as function/lambda scope (though calls must be indirect, e.g. virtual or std::function):
+//
+// struct {
+// std::mutex mutex;
+// int x FTL_ATTRIBUTE(guarded_by(mutex)) = -1;
+//
+// int f() {
+// {
+// ftl::FakeGuard guard(mutex);
+// x = 0;
+// }
+//
+// return FTL_FAKE_GUARD(mutex, x + 1);
+// }
+//
+// std::function<int()> g() const {
+// return [this]() FTL_FAKE_GUARD(mutex) { return x; };
+// }
+// } s;
+//
+// assert(s.f() == 1);
+// assert(s.g()() == 0);
+//
+// An example of a situation where FakeGuard helps is a mutex that guards writes on Thread 1, and
+// reads on Thread 2. Reads on Thread 1, which is the only writer, need not be under lock, so can
+// use FakeGuard to appease the thread safety analyzer. Another example is enforcing and documenting
+// exclusive access by a single thread. This is done by defining a global constant that represents a
+// thread context, and annotating guarded variables as if it were a mutex (though without any effect
+// at run time):
+//
+// constexpr class [[clang::capability("mutex")]] {
+// } kMainThreadContext;
+//
+template <typename Mutex>
+struct [[clang::scoped_lockable]] FakeGuard final {
+ explicit FakeGuard(const Mutex& mutex) FTL_ATTRIBUTE(acquire_capability(mutex)) {}
+ [[clang::release_capability()]] ~FakeGuard() {}
+
+ FakeGuard(const FakeGuard&) = delete;
+ FakeGuard& operator=(const FakeGuard&) = delete;
+};
+
+} // namespace android::ftl
+
+// TODO: Enable in C++23 once standard attributes can be used on lambdas.
+#if 0
+#define FTL_FAKE_GUARD1(mutex) [[using clang: acquire_capability(mutex), release_capability(mutex)]]
+#else
+#define FTL_FAKE_GUARD1(mutex) \
+ FTL_ATTRIBUTE(acquire_capability(mutex)) \
+ FTL_ATTRIBUTE(release_capability(mutex))
+#endif
+
+// The parentheses around `expr` are needed to deduce an lvalue or rvalue reference.
+#define FTL_FAKE_GUARD2(mutex, expr) \
+ [&]() -> decltype(auto) { \
+ const android::ftl::FakeGuard guard(mutex); \
+ return (expr); \
+ }()
+
+#define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard
+
+// The void argument suppresses a warning about zero variadic macro arguments.
+#define FTL_FAKE_GUARD(...) \
+ FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, void)(__VA_ARGS__)
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
new file mode 100644
index 0000000..7c3b29b
--- /dev/null
+++ b/include/input/PrintTools.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+namespace android {
+
+template <typename T>
+std::string constToString(const T& v) {
+ return std::to_string(v);
+}
+
+/**
+ * Convert a set of integral types to string.
+ */
+template <typename T>
+std::string dumpSet(const std::set<T>& v, std::string (*toString)(const T&) = constToString) {
+ std::string out;
+ for (const T& entry : v) {
+ out += out.empty() ? "{" : ", ";
+ out += toString(entry);
+ }
+ return out.empty() ? "{}" : (out + "}");
+}
+
+/**
+ * Convert a map to string. Both keys and values of the map should be integral type.
+ */
+template <typename K, typename V>
+std::string dumpMap(const std::map<K, V>& map, std::string (*keyToString)(const K&) = constToString,
+ std::string (*valueToString)(const V&) = constToString) {
+ std::string out;
+ for (const auto& [k, v] : map) {
+ if (!out.empty()) {
+ out += "\n";
+ }
+ out += keyToString(k) + ":" + valueToString(v);
+ }
+ return out;
+}
+
+const char* toString(bool value);
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index a1b08db..d952dc7 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -57,8 +57,7 @@
}
virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
- int32_t capability)
- {
+ int32_t capability) {
Parcel data, reply;
data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
data.writeInt32((int32_t) uid);
@@ -67,6 +66,12 @@
data.writeInt32(capability);
remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual void onUidProcAdjChanged(uid_t uid) {
+ Parcel data, reply;
+ data.writeInt32((int32_t)uid);
+ remote()->transact(ON_UID_PROC_ADJ_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
// ----------------------------------------------------------------------
@@ -102,6 +107,7 @@
onUidIdle(uid, disabled);
return NO_ERROR;
} break;
+
case ON_UID_STATE_CHANGED_TRANSACTION: {
CHECK_INTERFACE(IUidObserver, data, reply);
uid_t uid = data.readInt32();
@@ -111,6 +117,14 @@
onUidStateChanged(uid, procState, procStateSeq, capability);
return NO_ERROR;
} break;
+
+ case ON_UID_PROC_ADJ_CHANGED_TRANSACTION: {
+ CHECK_INTERFACE(IUidObserver, data, reply);
+ uid_t uid = data.readInt32();
+ onUidProcAdjChanged(uid);
+ return NO_ERROR;
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index be50a75..58b0b35 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1584,6 +1584,7 @@
template<class T>
status_t Parcel::readAligned(T *pArg) const {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(std::is_trivially_copyable_v<T>);
if ((mDataPos+sizeof(T)) <= mDataSize) {
if (mObjectsSize > 0) {
@@ -1595,9 +1596,8 @@
}
}
- const void* data = mData+mDataPos;
+ memcpy(pArg, mData + mDataPos, sizeof(T));
mDataPos += sizeof(T);
- *pArg = *reinterpret_cast<const T*>(data);
return NO_ERROR;
} else {
return NOT_ENOUGH_DATA;
@@ -1617,10 +1617,11 @@
template<class T>
status_t Parcel::writeAligned(T val) {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(std::is_trivially_copyable_v<T>);
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
- *reinterpret_cast<T*>(mData+mDataPos) = val;
+ memcpy(mData + mDataPos, &val, sizeof(val));
return finishWrite(sizeof(val));
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 2e7084e..6d89064 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -313,7 +313,8 @@
const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) {
for (int i = 0; i < niovs; i++) {
- LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ 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());
}
@@ -343,7 +344,8 @@
}
for (int i = 0; i < niovs; i++) {
- LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ 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());
}
return OK;
@@ -660,8 +662,14 @@
status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
uint8_t buf;
- while (connection->rpcTransport->peek(&buf, sizeof(buf)).value_or(0) > 0) {
- status_t status = getAndExecuteCommand(connection, session, type);
+ while (true) {
+ size_t num_bytes;
+ status_t status = connection->rpcTransport->peek(&buf, sizeof(buf), &num_bytes);
+ if (status == WOULD_BLOCK) break;
+ if (status != OK) return status;
+ if (!num_bytes) break;
+
+ status = getAndExecuteCommand(connection, session, type);
if (status != OK) return status;
}
return OK;
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 636e5d0..7cfc780 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -24,9 +24,6 @@
#include "FdTrigger.h"
#include "RpcState.h"
-using android::base::ErrnoError;
-using android::base::Result;
-
namespace android {
namespace {
@@ -35,12 +32,20 @@
class RpcTransportRaw : public RpcTransport {
public:
explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
- Result<size_t> peek(void *buf, size_t size) override {
+ status_t peek(void* buf, size_t size, size_t* out_size) override {
ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK));
if (ret < 0) {
- return ErrnoError() << "recv(MSG_PEEK)";
+ int savedErrno = errno;
+ if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) {
+ return WOULD_BLOCK;
+ }
+
+ LOG_RPC_DETAIL("RpcTransport peek(): %s", strerror(savedErrno));
+ return -savedErrno;
}
- return ret;
+
+ *out_size = static_cast<size_t>(ret);
+ return OK;
}
template <typename SendOrReceive>
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 3936204..bc68c37 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -37,10 +37,6 @@
#define LOG_TLS_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
#endif
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-
namespace android {
namespace {
@@ -165,17 +161,8 @@
return ret;
}
- // |sslError| should be from Ssl::getError().
- // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
- // return error. Also return error if |fdTrigger| is triggered before or during poll().
- status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
- const char* fnString, int additionalEvent,
- const std::function<status_t()>& altPoll) {
+ status_t toStatus(int sslError, const char* fnString) {
switch (sslError) {
- case SSL_ERROR_WANT_READ:
- return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
- case SSL_ERROR_WANT_WRITE:
- return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll);
case SSL_ERROR_SYSCALL: {
auto queue = toString();
LOG_TLS_DETAIL("%s(): %s. Treating as DEAD_OBJECT. Error queue: %s", fnString,
@@ -191,6 +178,22 @@
}
}
+ // |sslError| should be from Ssl::getError().
+ // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
+ // return error. Also return error if |fdTrigger| is triggered before or during poll().
+ status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
+ const char* fnString, int additionalEvent,
+ const std::function<status_t()>& altPoll) {
+ switch (sslError) {
+ case SSL_ERROR_WANT_READ:
+ return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
+ case SSL_ERROR_WANT_WRITE:
+ return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll);
+ default:
+ return toStatus(sslError, fnString);
+ }
+ }
+
private:
bool mHandled = false;
@@ -274,7 +277,7 @@
public:
RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
: mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
- Result<size_t> peek(void* buf, size_t size) override;
+ status_t peek(void* buf, size_t size, size_t* out_size) override;
status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) override;
status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
@@ -286,7 +289,7 @@
};
// Error code is errno.
-Result<size_t> RpcTransportTls::peek(void* buf, size_t size) {
+status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_size) {
size_t todo = std::min<size_t>(size, std::numeric_limits<int>::max());
auto [ret, errorQueue] = mSsl.call(SSL_peek, buf, static_cast<int>(todo));
if (ret < 0) {
@@ -294,13 +297,15 @@
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
// Seen EAGAIN / EWOULDBLOCK on recv(2) / send(2).
// Like RpcTransportRaw::peek(), don't handle it here.
- return Error(EWOULDBLOCK) << "SSL_peek(): " << errorQueue.toString();
+ errorQueue.clear();
+ return WOULD_BLOCK;
}
- return Error() << "SSL_peek(): " << errorQueue.toString();
+ return errorQueue.toStatus(err, "SSL_peek");
}
errorQueue.clear();
LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret);
- return ret;
+ *out_size = static_cast<size_t>(ret);
+ return OK;
}
status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index ade2d94..751c4f9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -22,7 +22,6 @@
#include <memory>
#include <string>
-#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <utils/Errors.h>
@@ -41,7 +40,7 @@
virtual ~RpcTransport() = default;
// replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled.
- [[nodiscard]] virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
+ [[nodiscard]] virtual status_t peek(void *buf, size_t size, size_t *out_size) = 0;
/**
* Read (or write), but allow to be interrupted by a trigger.
diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
index abc7f1d..5dfbd44 100644
--- a/libs/binder/include_activitymanager/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -31,20 +31,21 @@
class ActivityManager
{
public:
-
enum {
// Flag for registerUidObserver: report uid state changed
- UID_OBSERVER_PROCSTATE = 1<<0,
+ UID_OBSERVER_PROCSTATE = 1 << 0,
// Flag for registerUidObserver: report uid gone
- UID_OBSERVER_GONE = 1<<1,
+ UID_OBSERVER_GONE = 1 << 1,
// Flag for registerUidObserver: report uid has become idle
- UID_OBSERVER_IDLE = 1<<2,
+ UID_OBSERVER_IDLE = 1 << 2,
// Flag for registerUidObserver: report uid has become active
- UID_OBSERVER_ACTIVE = 1<<3,
+ UID_OBSERVER_ACTIVE = 1 << 3,
// Flag for registerUidObserver: report uid cached state has changed
- UID_OBSERVER_CACHED = 1<<4,
+ UID_OBSERVER_CACHED = 1 << 4,
// Flag for registerUidObserver: report uid capability has changed
- UID_OBSERVER_CAPABILITY = 1<<5,
+ UID_OBSERVER_CAPABILITY = 1 << 5,
+ // Flag for registerUidObserver: report pid oom adj has changed
+ UID_OBSERVER_PROC_OOM_ADJ = 1 << 6,
};
// PROCESS_STATE_* must come from frameworks/base/core/java/android/app/ProcessStateEnum.aidl.
diff --git a/libs/binder/include_activitymanager/binder/IUidObserver.h b/libs/binder/include_activitymanager/binder/IUidObserver.h
index 9291c0b..17f03a9 100644
--- a/libs/binder/include_activitymanager/binder/IUidObserver.h
+++ b/libs/binder/include_activitymanager/binder/IUidObserver.h
@@ -33,13 +33,15 @@
virtual void onUidActive(uid_t uid) = 0;
virtual void onUidIdle(uid_t uid, bool disabled) = 0;
virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
- int32_t capability) = 0;
+ int32_t capability) = 0;
+ virtual void onUidProcAdjChanged(uid_t uid) = 0;
enum {
ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
ON_UID_ACTIVE_TRANSACTION,
ON_UID_IDLE_TRANSACTION,
- ON_UID_STATE_CHANGED_TRANSACTION
+ ON_UID_STATE_CHANGED_TRANSACTION,
+ ON_UID_PROC_ADJ_CHANGED_TRANSACTION
};
};
diff --git a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
index 094addd..50d12c4 100644
--- a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
+++ b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
@@ -18,6 +18,7 @@
#include <memory>
#include <mutex>
+#include <vector>
#include <binder/RpcAuth.h>
#include <binder/RpcCertificateFormat.h>
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index bc2eb23..ef20196 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -18,6 +18,7 @@
"cast_test.cpp",
"concat_test.cpp",
"enum_test.cpp",
+ "fake_guard_test.cpp",
"future_test.cpp",
"small_map_test.cpp",
"small_vector_test.cpp",
@@ -29,13 +30,6 @@
"-Werror",
"-Wextra",
"-Wpedantic",
- ],
-
- header_libs: [
- "libbase_headers",
- ],
-
- shared_libs: [
- "libbase",
+ "-Wthread-safety",
],
}
diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp
index d8ce7a5..5592a01 100644
--- a/libs/ftl/enum_test.cpp
+++ b/libs/ftl/enum_test.cpp
@@ -143,6 +143,16 @@
EXPECT_EQ(ftl::flag_string(Flags::kNone), "0b0");
EXPECT_EQ(ftl::flag_string(Flags::kMask), "0b10010010");
EXPECT_EQ(ftl::flag_string(Flags::kAll), "0b11111111");
+
+ enum class Flags64 : std::uint64_t {
+ kFlag0 = 0b1ull,
+ kFlag63 = 0x8000'0000'0000'0000ull,
+ kMask = kFlag0 | kFlag63
+ };
+
+ EXPECT_EQ(ftl::flag_string(Flags64::kFlag0), "kFlag0");
+ EXPECT_EQ(ftl::flag_string(Flags64::kFlag63), "kFlag63");
+ EXPECT_EQ(ftl::flag_string(Flags64::kMask), "0x8000000000000001");
}
{
EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth");
diff --git a/libs/ftl/fake_guard_test.cpp b/libs/ftl/fake_guard_test.cpp
new file mode 100644
index 0000000..9d36e69
--- /dev/null
+++ b/libs/ftl/fake_guard_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/fake_guard.h>
+#include <gtest/gtest.h>
+
+#include <functional>
+#include <mutex>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(FakeGuard, Example) {
+ struct {
+ std::mutex mutex;
+ int x FTL_ATTRIBUTE(guarded_by(mutex)) = -1;
+
+ int f() {
+ {
+ ftl::FakeGuard guard(mutex);
+ x = 0;
+ }
+
+ return FTL_FAKE_GUARD(mutex, x + 1);
+ }
+
+ std::function<int()> g() const {
+ return [this]() FTL_FAKE_GUARD(mutex) { return x; };
+ }
+ } s;
+
+ EXPECT_EQ(s.f(), 1);
+ EXPECT_EQ(s.g()(), 0);
+}
+
+} // namespace android::test
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 34db5b1..304fc17 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -134,6 +134,7 @@
SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeBool, autoRefresh);
+ SAFE_PARCEL(output.writeBool, dimmingEnabled);
SAFE_PARCEL(output.writeUint32, blurRegions.size());
for (auto region : blurRegions) {
@@ -243,6 +244,7 @@
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readBool, &autoRefresh);
+ SAFE_PARCEL(input.readBool, &dimmingEnabled);
uint32_t numRegions = 0;
SAFE_PARCEL(input.readUint32, &numRegions);
@@ -598,6 +600,10 @@
what |= eColorSpaceAgnosticChanged;
colorSpaceAgnostic = other.colorSpaceAgnostic;
}
+ if (other.what & eDimmingEnabledChanged) {
+ what |= eDimmingEnabledChanged;
+ dimmingEnabled = other.dimmingEnabled;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 52a22a7..6c197c4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1195,6 +1195,20 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDimmingEnabled(
+ const sp<SurfaceControl>& sc, bool dimmingEnabled) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eDimmingEnabledChanged;
+ s->dimmingEnabled = dimmingEnabled;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha(
const sp<SurfaceControl>& sc, float alpha) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 0f37dab..4ca8d68 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -150,7 +150,7 @@
eTransparentRegionChanged = 0x00000020,
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
- /* unused 0x00000400, */
+ eDimmingEnabledChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
/* unused 0x00001000, */
eBufferCropChanged = 0x00002000,
@@ -187,7 +187,7 @@
eAutoRefreshChanged = 0x1000'00000000,
eStretchChanged = 0x2000'00000000,
eTrustedOverlayChanged = 0x4000'00000000,
- eDropInputModeChanged = 0x8000'00000000,
+ eDropInputModeChanged = 0x8000'00000000
};
layer_state_t();
@@ -298,6 +298,8 @@
// Force inputflinger to drop all input events for the layer and its children.
gui::DropInputMode dropInputMode;
+
+ bool dimmingEnabled;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 9d03f58..0cc43d8 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -491,6 +491,7 @@
uint32_t flags, uint32_t mask);
Transaction& setTransparentRegionHint(const sp<SurfaceControl>& sc,
const Region& transparentRegion);
+ Transaction& setDimmingEnabled(const sp<SurfaceControl>& sc, bool dimmingEnabled);
Transaction& setAlpha(const sp<SurfaceControl>& sc,
float alpha);
Transaction& setMatrix(const sp<SurfaceControl>& sc,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 18fb7c1..1d4fc1f 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -50,6 +50,7 @@
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
+ "PrintTools.cpp",
"PropertyMap.cpp",
"TouchVideoFrame.cpp",
"VelocityControl.cpp",
@@ -102,6 +103,9 @@
sanitize: {
misc_undefined: ["integer"],
+ diag: {
+ misc_undefined: ["integer"],
+ },
},
},
host: {
diff --git a/libs/input/PrintTools.cpp b/libs/input/PrintTools.cpp
new file mode 100644
index 0000000..5d6ae4e
--- /dev/null
+++ b/libs/input/PrintTools.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "PrintTools"
+
+#include <input/PrintTools.h>
+
+namespace android {
+
+const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
+} // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index a6465ee..7f427f2 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -15,13 +15,6 @@
*/
#define LOG_TAG "VelocityTracker"
-//#define LOG_NDEBUG 0
-
-// Log debug messages about velocity tracking.
-static constexpr bool DEBUG_VELOCITY = false;
-
-// Log debug messages about the progress of the algorithm itself.
-static constexpr bool DEBUG_STRATEGY = false;
#include <array>
#include <inttypes.h>
@@ -36,6 +29,27 @@
namespace android {
+/**
+ * Log debug messages about velocity tracking.
+ * Enable this via "adb shell setprop log.tag.VelocityTrackerVelocity DEBUG" (requires restart)
+ */
+const bool DEBUG_VELOCITY =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Velocity", ANDROID_LOG_INFO);
+
+/**
+ * Log debug messages about the progress of the algorithm itself.
+ * Enable this via "adb shell setprop log.tag.VelocityTrackerStrategy DEBUG" (requires restart)
+ */
+const bool DEBUG_STRATEGY =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Strategy", ANDROID_LOG_INFO);
+
+/**
+ * Log debug messages about the 'impulse' strategy.
+ * Enable this via "adb shell setprop log.tag.VelocityTrackerImpulse DEBUG" (requires restart)
+ */
+const bool DEBUG_IMPULSE =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Impulse", ANDROID_LOG_INFO);
+
// Nanoseconds per milliseconds.
static const nsecs_t NANOS_PER_MS = 1000000;
@@ -141,7 +155,7 @@
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
case VelocityTracker::Strategy::LSQ2:
- if (DEBUG_STRATEGY) {
+ if (DEBUG_STRATEGY && !DEBUG_IMPULSE) {
ALOGI("Initializing lsq2 strategy");
}
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
@@ -1172,7 +1186,25 @@
outEstimator->degree = 2; // similar results to 2nd degree fit
outEstimator->confidence = 1;
if (DEBUG_STRATEGY) {
- ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+ ALOGD("velocity: (%.1f, %.1f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+ }
+ if (DEBUG_IMPULSE) {
+ // TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
+ // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons
+ VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
+ BitSet32 idBits;
+ const uint32_t pointerId = 0;
+ idBits.markBit(pointerId);
+ for (ssize_t i = m - 1; i >= 0; i--) {
+ lsq2.addMovement(time[i], idBits, {{x[i], y[i]}});
+ }
+ float outVx = 0, outVy = 0;
+ const bool computed = lsq2.getVelocity(pointerId, &outVx, &outVy);
+ if (computed) {
+ ALOGD("lsq2 velocity: (%.1f, %.1f)", outVx, outVy);
+ } else {
+ ALOGD("lsq2 velocity: could not compute velocity");
+ }
}
return true;
}
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 381900e..4a1784e 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -207,7 +207,11 @@
if (result == 0) {
outPlanes->planeCount = 3;
outPlanes->planes[0].data = yuvData.y;
- outPlanes->planes[0].pixelStride = 1;
+ if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010) {
+ outPlanes->planes[0].pixelStride = 2;
+ } else {
+ outPlanes->planes[0].pixelStride = 1;
+ }
outPlanes->planes[0].rowStride = yuvData.ystride;
outPlanes->planes[1].data = yuvData.cb;
outPlanes->planes[1].pixelStride = yuvData.chroma_step;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 1665550..df9f8ab 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -39,6 +39,7 @@
#include <gui/TraceUtils.h>
#include <sync/sync.h>
#include <ui/BlurRegion.h>
+#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
@@ -1105,9 +1106,15 @@
.requiresLinearEffect = requiresLinearEffect,
.layerDimmingRatio = layerDimmingRatio}));
- // Turn on dithering when dimming beyond this threshold.
+ // Turn on dithering when dimming beyond this (arbitrary) threshold...
static constexpr float kDimmingThreshold = 0.2f;
- if (layerDimmingRatio <= kDimmingThreshold) {
+ // ...or we're rendering an HDR layer down to an 8-bit target
+ // Most HDR standards require at least 10-bits of color depth for source content, so we
+ // can just extract the transfer function rather than dig into precise gralloc layout.
+ // Furthermore, we can assume that the only 8-bit target we support is RGBA8888.
+ const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) &&
+ buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888;
+ if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) {
paint.setDither(true);
}
paint.setAlphaf(layer.alpha);
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 8de91da..1fce31d 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -359,20 +359,19 @@
if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
continue;
}
- if (0 != planeLayoutComponent.offsetInBits % 8) {
- unlock(bufferHandle);
- return BAD_VALUE;
- }
- uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes +
- (planeLayoutComponent.offsetInBits / 8);
+ uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes;
+
+ // Note that `offsetInBits` may not be a multiple of 8 for packed formats (e.g. P010)
+ // but we still want to point to the start of the first byte.
+ tmpData += (planeLayoutComponent.offsetInBits / 8);
+
uint64_t sampleIncrementInBytes;
auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
switch (type) {
case PlaneLayoutComponentType::Y:
- if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) ||
- (planeLayout.sampleIncrementInBits != 8)) {
+ if ((ycbcr.y != nullptr) || (planeLayout.sampleIncrementInBits % 8 != 0)) {
unlock(bufferHandle);
return BAD_VALUE;
}
@@ -388,7 +387,8 @@
}
sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
- if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) {
+ if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2) &&
+ (sampleIncrementInBytes != 4)) {
unlock(bufferHandle);
return BAD_VALUE;
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ed9bfd2..41878e3 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -58,6 +58,7 @@
srcs: [
"InputClassifier.cpp",
"InputCommonConverter.cpp",
+ "PreferStylusOverTouchBlocker.cpp",
"UnwantedInteractionBlocker.cpp",
"InputManager.cpp",
],
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 3a4b6c5..2a3924b 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -202,9 +202,11 @@
coords += "}";
}
return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
- ", source=%s, action=%s, pointerCount=%" PRIu32 " pointers=%s)",
+ ", source=%s, action=%s, pointerCount=%" PRIu32
+ " pointers=%s, flags=0x%08x)",
id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
- MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str());
+ MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
+ flags);
}
void NotifyMotionArgs::notify(InputListenerInterface& listener) const {
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
new file mode 100644
index 0000000..beec2e1
--- /dev/null
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PreferStylusOverTouchBlocker.h"
+#include <input/PrintTools.h>
+
+namespace android {
+
+static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) {
+ bool hasStylus = false;
+ bool hasTouch = false;
+ for (size_t i = 0; i < args.pointerCount; i++) {
+ // Make sure we are canceling stylus pointers
+ const int32_t toolType = args.pointerProperties[i].toolType;
+ if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+ toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ hasStylus = true;
+ }
+ if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
+ hasTouch = true;
+ }
+ }
+ return std::make_pair(hasTouch, hasStylus);
+}
+
+/**
+ * Intersect two sets in-place, storing the result in 'set1'.
+ * Find elements in set1 that are not present in set2 and delete them,
+ * relying on the fact that the two sets are ordered.
+ */
+template <typename T>
+static void intersectInPlace(std::set<T>& set1, const std::set<T>& set2) {
+ typename std::set<T>::iterator it1 = set1.begin();
+ typename std::set<T>::const_iterator it2 = set2.begin();
+ while (it1 != set1.end() && it2 != set2.end()) {
+ const T& element1 = *it1;
+ const T& element2 = *it2;
+ if (element1 < element2) {
+ // This element is not present in set2. Remove it from set1.
+ it1 = set1.erase(it1);
+ continue;
+ }
+ if (element2 < element1) {
+ it2++;
+ }
+ if (element1 == element2) {
+ it1++;
+ it2++;
+ }
+ }
+ // Remove the rest of the elements in set1 because set2 is already exhausted.
+ set1.erase(it1, set1.end());
+}
+
+/**
+ * Same as above, but prune a map
+ */
+template <typename K, class V>
+static void intersectInPlace(std::map<K, V>& map, const std::set<K>& set2) {
+ typename std::map<K, V>::iterator it1 = map.begin();
+ typename std::set<K>::const_iterator it2 = set2.begin();
+ while (it1 != map.end() && it2 != set2.end()) {
+ const auto& [key, _] = *it1;
+ const K& element2 = *it2;
+ if (key < element2) {
+ // This element is not present in set2. Remove it from map.
+ it1 = map.erase(it1);
+ continue;
+ }
+ if (element2 < key) {
+ it2++;
+ }
+ if (key == element2) {
+ it1++;
+ it2++;
+ }
+ }
+ // Remove the rest of the elements in map because set2 is already exhausted.
+ map.erase(it1, map.end());
+}
+
+// -------------------------------- PreferStylusOverTouchBlocker -----------------------------------
+
+std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion(
+ const NotifyMotionArgs& args) {
+ const auto [hasTouch, hasStylus] = checkToolType(args);
+ const bool isUpOrCancel =
+ args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL;
+
+ if (hasTouch && hasStylus) {
+ mDevicesWithMixedToolType.insert(args.deviceId);
+ }
+ // Handle the case where mixed touch and stylus pointers are reported. Add this device to the
+ // ignore list, since it clearly supports simultaneous touch and stylus.
+ if (mDevicesWithMixedToolType.find(args.deviceId) != mDevicesWithMixedToolType.end()) {
+ // This event comes from device with mixed stylus and touch event. Ignore this device.
+ if (mCanceledDevices.find(args.deviceId) != mCanceledDevices.end()) {
+ // If we started to cancel events from this device, continue to do so to keep
+ // the stream consistent. It should happen at most once per "mixed" device.
+ if (isUpOrCancel) {
+ mCanceledDevices.erase(args.deviceId);
+ mLastTouchEvents.erase(args.deviceId);
+ }
+ return {};
+ }
+ return {args};
+ }
+
+ const bool isStylusEvent = hasStylus;
+ const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
+
+ if (isStylusEvent) {
+ if (isDown) {
+ // Reject all touch while stylus is down
+ mActiveStyli.insert(args.deviceId);
+
+ // Cancel all current touch!
+ std::vector<NotifyMotionArgs> result;
+ for (auto& [deviceId, lastTouchEvent] : mLastTouchEvents) {
+ if (mCanceledDevices.find(deviceId) != mCanceledDevices.end()) {
+ // Already canceled, go to next one.
+ continue;
+ }
+ // Not yet canceled. Cancel it.
+ lastTouchEvent.action = AMOTION_EVENT_ACTION_CANCEL;
+ lastTouchEvent.flags |= AMOTION_EVENT_FLAG_CANCELED;
+ lastTouchEvent.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ result.push_back(lastTouchEvent);
+ mCanceledDevices.insert(deviceId);
+ }
+ result.push_back(args);
+ return result;
+ }
+ if (isUpOrCancel) {
+ mActiveStyli.erase(args.deviceId);
+ }
+ // Never drop stylus events
+ return {args};
+ }
+
+ const bool isTouchEvent = hasTouch;
+ if (isTouchEvent) {
+ // Suppress the current gesture if any stylus is still down
+ if (!mActiveStyli.empty()) {
+ mCanceledDevices.insert(args.deviceId);
+ }
+
+ const bool shouldDrop = mCanceledDevices.find(args.deviceId) != mCanceledDevices.end();
+ if (isUpOrCancel) {
+ mCanceledDevices.erase(args.deviceId);
+ mLastTouchEvents.erase(args.deviceId);
+ }
+
+ // If we already canceled the current gesture, then continue to drop events from it, even if
+ // the stylus has been lifted.
+ if (shouldDrop) {
+ return {};
+ }
+
+ if (!isUpOrCancel) {
+ mLastTouchEvents[args.deviceId] = args;
+ }
+ return {args};
+ }
+
+ // Not a touch or stylus event
+ return {args};
+}
+
+void PreferStylusOverTouchBlocker::notifyInputDevicesChanged(
+ const std::vector<InputDeviceInfo>& inputDevices) {
+ std::set<int32_t> presentDevices;
+ for (const InputDeviceInfo& device : inputDevices) {
+ presentDevices.insert(device.getId());
+ }
+ // Only keep the devices that are still present.
+ intersectInPlace(mDevicesWithMixedToolType, presentDevices);
+ intersectInPlace(mLastTouchEvents, presentDevices);
+ intersectInPlace(mCanceledDevices, presentDevices);
+ intersectInPlace(mActiveStyli, presentDevices);
+}
+
+void PreferStylusOverTouchBlocker::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ mDevicesWithMixedToolType.erase(args.deviceId);
+ mLastTouchEvents.erase(args.deviceId);
+ mCanceledDevices.erase(args.deviceId);
+ mActiveStyli.erase(args.deviceId);
+}
+
+static std::string dumpArgs(const NotifyMotionArgs& args) {
+ return args.dump();
+}
+
+std::string PreferStylusOverTouchBlocker::dump() const {
+ std::string out;
+ out += "mActiveStyli: " + dumpSet(mActiveStyli) + "\n";
+ out += "mLastTouchEvents: " + dumpMap(mLastTouchEvents, constToString, dumpArgs) + "\n";
+ out += "mDevicesWithMixedToolType: " + dumpSet(mDevicesWithMixedToolType) + "\n";
+ out += "mCanceledDevices: " + dumpSet(mCanceledDevices) + "\n";
+ return out;
+}
+
+} // namespace android
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.h b/services/inputflinger/PreferStylusOverTouchBlocker.h
new file mode 100644
index 0000000..716dc4d
--- /dev/null
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <set>
+#include "InputListener.h"
+
+namespace android {
+
+/**
+ * When stylus is down, all touch is ignored.
+ * TODO(b/210159205): delete this when simultaneous stylus and touch is supported
+ */
+class PreferStylusOverTouchBlocker {
+public:
+ /**
+ * Process the provided event and emit 0 or more events that should be used instead of it.
+ * In the majority of cases, the returned result will just be the provided args (array with
+ * only 1 element), unmodified.
+ *
+ * If the gesture should be blocked, the returned result may be:
+ *
+ * a) An empty array, if the current event should just be ignored completely
+ * b) An array of N elements, containing N-1 events with ACTION_CANCEL and the current event.
+ *
+ * The returned result is intended to be reinjected into the original event stream in
+ * replacement of the incoming event.
+ */
+ std::vector<NotifyMotionArgs> processMotion(const NotifyMotionArgs& args);
+ std::string dump() const;
+
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
+
+ void notifyDeviceReset(const NotifyDeviceResetArgs& args);
+
+private:
+ // Stores the device id's of styli that are currently down.
+ std::set<int32_t /*deviceId*/> mActiveStyli;
+ // For each device, store the last touch event as long as the touch is down. Upon liftoff,
+ // the entry is erased.
+ std::map<int32_t /*deviceId*/, NotifyMotionArgs> mLastTouchEvents;
+ // Device ids of devices for which the current touch gesture is canceled.
+ std::set<int32_t /*deviceId*/> mCanceledDevices;
+
+ // Device ids of input devices where we encountered simultaneous touch and stylus
+ // events. For these devices, we don't do any event processing (nothing is blocked or altered).
+ std::set<int32_t /*deviceId*/> mDevicesWithMixedToolType;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 64dbb8c..b69e16a 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -44,7 +44,8 @@
}
static bool isFromTouchscreen(int32_t source) {
- return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN);
+ return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN) &&
+ !isFromSource(source, AINPUT_SOURCE_STYLUS);
}
static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) {
@@ -367,6 +368,14 @@
}
void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) {
+ const std::vector<NotifyMotionArgs> processedArgs =
+ mPreferStylusOverTouchBlocker.processMotion(*args);
+ for (const NotifyMotionArgs& loopArgs : processedArgs) {
+ notifyMotionInner(&loopArgs);
+ }
+}
+
+void UnwantedInteractionBlocker::notifyMotionInner(const NotifyMotionArgs* args) {
auto it = mPalmRejectors.find(args->deviceId);
const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source);
if (!sendToPalmRejector) {
@@ -400,6 +409,7 @@
mPalmRejectors.emplace(args->deviceId, info);
}
mListener.notifyDeviceReset(args);
+ mPreferStylusOverTouchBlocker.notifyDeviceReset(*args);
}
void UnwantedInteractionBlocker::notifyPointerCaptureChanged(
@@ -436,10 +446,13 @@
auto const& [deviceId, _] = item;
return devicesToKeep.find(deviceId) == devicesToKeep.end();
});
+ mPreferStylusOverTouchBlocker.notifyInputDevicesChanged(inputDevices);
}
void UnwantedInteractionBlocker::dump(std::string& dump) {
dump += "UnwantedInteractionBlocker:\n";
+ dump += " mPreferStylusOverTouchBlocker:\n";
+ dump += addPrefix(mPreferStylusOverTouchBlocker.dump(), " ");
dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection));
dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n",
toString(isPalmRejectionEnabled()));
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index 14068fd..8a1cd72 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -23,6 +23,8 @@
#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h"
#include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h"
+#include "PreferStylusOverTouchBlocker.h"
+
namespace android {
// --- Functions for manipulation of event streams
@@ -88,9 +90,14 @@
InputListenerInterface& mListener;
const bool mEnablePalmRejection;
+ // When stylus is down, ignore touch
+ PreferStylusOverTouchBlocker mPreferStylusOverTouchBlocker;
+
// Detect and reject unwanted palms on screen
// Use a separate palm rejector for every touch device.
std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors;
+ // TODO(b/210159205): delete this when simultaneous stylus and touch is supported
+ void notifyMotionInner(const NotifyMotionArgs* args);
};
class SlotState {
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 9691ad8..32eec29 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -263,15 +263,15 @@
static void benchmarkNotifyMotion(benchmark::State& state) {
// Create dispatcher
sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
- std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
- dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
- dispatcher->start();
+ InputDispatcher dispatcher(fakePolicy);
+ dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher.start();
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window");
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
- dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
NotifyMotionArgs motionArgs = generateMotionArgs();
@@ -280,55 +280,79 @@
motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
motionArgs.downTime = now();
motionArgs.eventTime = motionArgs.downTime;
- dispatcher->notifyMotion(&motionArgs);
+ dispatcher.notifyMotion(&motionArgs);
// Send ACTION_UP
motionArgs.action = AMOTION_EVENT_ACTION_UP;
motionArgs.eventTime = now();
- dispatcher->notifyMotion(&motionArgs);
+ dispatcher.notifyMotion(&motionArgs);
window->consumeEvent();
window->consumeEvent();
}
- dispatcher->stop();
+ dispatcher.stop();
}
static void benchmarkInjectMotion(benchmark::State& state) {
// Create dispatcher
sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
- std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
- dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
- dispatcher->start();
+ InputDispatcher dispatcher(fakePolicy);
+ dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher.start();
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window");
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
- dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
for (auto _ : state) {
MotionEvent event = generateMotionEvent();
// Send ACTION_DOWN
- dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
// Send ACTION_UP
event.setAction(AMOTION_EVENT_ACTION_UP);
- dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
window->consumeEvent();
window->consumeEvent();
}
- dispatcher->stop();
+ dispatcher.stop();
+}
+
+static void benchmarkOnWindowInfosChanged(benchmark::State& state) {
+ // Create dispatcher
+ sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+ InputDispatcher dispatcher(fakePolicy);
+ dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher.start();
+
+ // Create a window
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+ std::vector<gui::WindowInfo> windowInfos{*window->getInfo()};
+ gui::DisplayInfo info;
+ info.displayId = window->getInfo()->displayId;
+ std::vector<gui::DisplayInfo> displayInfos{info};
+
+ for (auto _ : state) {
+ dispatcher.onWindowInfosChanged(windowInfos, displayInfos);
+ dispatcher.onWindowInfosChanged({} /*windowInfos*/, {} /*displayInfos*/);
+ }
+ dispatcher.stop();
}
BENCHMARK(benchmarkNotifyMotion);
BENCHMARK(benchmarkInjectMotion);
+BENCHMARK(benchmarkOnWindowInfosChanged);
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c8a3ccf..6bab349 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2350,8 +2350,8 @@
*touchedWindow.windowHandle->getInfo()) ||
(touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0;
})) {
- ALOGI("Dropping event because there is no touched window on display %d to receive it.",
- displayId);
+ ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
+ displayId, entry.getDescription().c_str());
injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 9d200bd..76a7c19 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -47,6 +47,7 @@
"InputReader_test.cpp",
"InputFlingerService_test.cpp",
"LatencyTracker_test.cpp",
+ "PreferStylusOverTouch_test.cpp",
"TestInputListener.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
diff --git a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
new file mode 100644
index 0000000..8e2ab88
--- /dev/null
+++ b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include "../PreferStylusOverTouchBlocker.h"
+
+namespace android {
+
+constexpr int32_t TOUCH_DEVICE_ID = 3;
+constexpr int32_t SECOND_TOUCH_DEVICE_ID = 4;
+constexpr int32_t STYLUS_DEVICE_ID = 5;
+constexpr int32_t SECOND_STYLUS_DEVICE_ID = 6;
+
+constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN;
+constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE;
+constexpr int UP = AMOTION_EVENT_ACTION_UP;
+constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL;
+static constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+constexpr int32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
+constexpr int32_t STYLUS = AINPUT_SOURCE_STYLUS;
+
+struct PointerData {
+ float x;
+ float y;
+};
+
+static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action,
+ const std::vector<PointerData>& points,
+ uint32_t source) {
+ size_t pointerCount = points.size();
+ if (action == DOWN || action == UP) {
+ EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
+ }
+
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ const int32_t deviceId = isFromSource(source, TOUCHSCREEN) ? TOUCH_DEVICE_ID : STYLUS_DEVICE_ID;
+ const int32_t toolType = isFromSource(source, TOUCHSCREEN) ? AMOTION_EVENT_TOOL_TYPE_FINGER
+ : AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerProperties[i].toolType = toolType;
+
+ pointerCoords[i].clear();
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y);
+ }
+
+ // Currently, can't have STYLUS source without it also being a TOUCH source. Update the source
+ // accordingly.
+ if (isFromSource(source, STYLUS)) {
+ source |= TOUCHSCREEN;
+ }
+
+ // Define a valid motion event.
+ NotifyMotionArgs args(/* id */ 0, eventTime, 0 /*readTime*/, deviceId, source, 0 /*displayId*/,
+ POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0,
+ /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {});
+
+ return args;
+}
+
+class PreferStylusOverTouchTest : public testing::Test {
+protected:
+ void assertNotBlocked(const NotifyMotionArgs& args) { assertResponse(args, {args}); }
+
+ void assertDropped(const NotifyMotionArgs& args) { assertResponse(args, {}); }
+
+ void assertResponse(const NotifyMotionArgs& args,
+ const std::vector<NotifyMotionArgs>& expected) {
+ std::vector<NotifyMotionArgs> receivedArgs = mBlocker.processMotion(args);
+ ASSERT_EQ(expected.size(), receivedArgs.size());
+ for (size_t i = 0; i < expected.size(); i++) {
+ // The 'eventTime' of CANCEL events is dynamically generated. Don't check this field.
+ if (expected[i].action == CANCEL && receivedArgs[i].action == CANCEL) {
+ receivedArgs[i].eventTime = expected[i].eventTime;
+ }
+
+ ASSERT_EQ(expected[i], receivedArgs[i])
+ << expected[i].dump() << " vs " << receivedArgs[i].dump();
+ }
+ }
+
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& devices) {
+ mBlocker.notifyInputDevicesChanged(devices);
+ }
+
+ void dump() const { ALOGI("Blocker: \n%s\n", mBlocker.dump().c_str()); }
+
+private:
+ PreferStylusOverTouchBlocker mBlocker;
+};
+
+TEST_F(PreferStylusOverTouchTest, TouchGestureIsNotBlocked) {
+ NotifyMotionArgs args;
+
+ args = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 3}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{1, 3}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+}
+
+TEST_F(PreferStylusOverTouchTest, StylusGestureIsNotBlocked) {
+ NotifyMotionArgs args;
+
+ args = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2}}, STYLUS);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 3}}, STYLUS);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{1, 3}}, STYLUS);
+ assertNotBlocked(args);
+}
+
+/**
+ * Existing touch gesture should be canceled when stylus goes down. There should be an ACTION_CANCEL
+ * event generated.
+ */
+TEST_F(PreferStylusOverTouchTest, TouchIsCanceledWhenStylusGoesDown) {
+ NotifyMotionArgs args;
+
+ args = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 3}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 30}}, STYLUS);
+ NotifyMotionArgs cancelArgs =
+ generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, CANCEL, {{1, 3}}, TOUCHSCREEN);
+ cancelArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
+ assertResponse(args, {cancelArgs, args});
+
+ // Both stylus and touch events continue. Stylus should be not blocked, and touch should be
+ // blocked
+ args = generateMotionArgs(3 /*downTime*/, 4 /*eventTime*/, MOVE, {{10, 31}}, STYLUS);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 5 /*eventTime*/, MOVE, {{1, 4}}, TOUCHSCREEN);
+ assertDropped(args);
+}
+
+/**
+ * Stylus goes down after touch gesture.
+ */
+TEST_F(PreferStylusOverTouchTest, StylusDownAfterTouch) {
+ NotifyMotionArgs args;
+
+ args = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 3}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{1, 3}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ // Stylus goes down
+ args = generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 30}}, STYLUS);
+ assertNotBlocked(args);
+}
+
+/**
+ * New touch events should be simply blocked (dropped) when stylus is down. No CANCEL event should
+ * be generated.
+ */
+TEST_F(PreferStylusOverTouchTest, NewTouchIsBlockedWhenStylusIsDown) {
+ NotifyMotionArgs args;
+ constexpr nsecs_t stylusDownTime = 0;
+ constexpr nsecs_t touchDownTime = 1;
+
+ args = generateMotionArgs(stylusDownTime, 0 /*eventTime*/, DOWN, {{10, 30}}, STYLUS);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(touchDownTime, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ // Stylus should continue to work
+ args = generateMotionArgs(stylusDownTime, 2 /*eventTime*/, MOVE, {{10, 31}}, STYLUS);
+ assertNotBlocked(args);
+
+ // Touch should continue to be blocked
+ args = generateMotionArgs(touchDownTime, 1 /*eventTime*/, MOVE, {{1, 3}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ args = generateMotionArgs(0 /*downTime*/, 5 /*eventTime*/, MOVE, {{1, 4}}, TOUCHSCREEN);
+ assertDropped(args);
+}
+
+/**
+ * New touch events should be simply blocked (dropped) when stylus is down. No CANCEL event should
+ * be generated.
+ */
+TEST_F(PreferStylusOverTouchTest, NewTouchWorksAfterStylusIsLifted) {
+ NotifyMotionArgs args;
+ constexpr nsecs_t stylusDownTime = 0;
+ constexpr nsecs_t touchDownTime = 4;
+
+ // Stylus goes down and up
+ args = generateMotionArgs(stylusDownTime, 0 /*eventTime*/, DOWN, {{10, 30}}, STYLUS);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(stylusDownTime, 2 /*eventTime*/, MOVE, {{10, 31}}, STYLUS);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(stylusDownTime, 3 /*eventTime*/, UP, {{10, 31}}, STYLUS);
+ assertNotBlocked(args);
+
+ // New touch goes down. It should not be blocked
+ args = generateMotionArgs(touchDownTime, touchDownTime, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(touchDownTime, 5 /*eventTime*/, MOVE, {{1, 3}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+
+ args = generateMotionArgs(touchDownTime, 6 /*eventTime*/, UP, {{1, 3}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+}
+
+/**
+ * Once a touch gesture is canceled, it should continue to be canceled, even if the stylus has been
+ * lifted.
+ */
+TEST_F(PreferStylusOverTouchTest, AfterStylusIsLiftedCurrentTouchIsBlocked) {
+ NotifyMotionArgs args;
+ constexpr nsecs_t stylusDownTime = 0;
+ constexpr nsecs_t touchDownTime = 1;
+
+ assertNotBlocked(generateMotionArgs(stylusDownTime, 0 /*eventTime*/, DOWN, {{10, 30}}, STYLUS));
+
+ args = generateMotionArgs(touchDownTime, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ // Lift the stylus
+ args = generateMotionArgs(stylusDownTime, 2 /*eventTime*/, UP, {{10, 30}}, STYLUS);
+ assertNotBlocked(args);
+
+ // Touch should continue to be blocked
+ args = generateMotionArgs(touchDownTime, 3 /*eventTime*/, MOVE, {{1, 3}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ args = generateMotionArgs(touchDownTime, 4 /*eventTime*/, UP, {{1, 3}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ // New touch should go through, though.
+ constexpr nsecs_t newTouchDownTime = 5;
+ args = generateMotionArgs(newTouchDownTime, 5 /*eventTime*/, DOWN, {{10, 20}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+}
+
+/**
+ * If an event with mixed stylus and touch pointers is encountered, it should be ignored. Touches
+ * from such should pass, even if stylus from the same device goes down.
+ */
+TEST_F(PreferStylusOverTouchTest, MixedStylusAndTouchPointersAreIgnored) {
+ NotifyMotionArgs args;
+
+ // Event from a stylus device, but with finger tool type
+ args = generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, STYLUS);
+ // Keep source stylus, but make the tool type touch
+ args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ assertNotBlocked(args);
+
+ // Second pointer (stylus pointer) goes down, from the same device
+ args = generateMotionArgs(1 /*downTime*/, 2 /*eventTime*/, POINTER_1_DOWN, {{1, 2}, {10, 20}},
+ STYLUS);
+ // Keep source stylus, but make the tool type touch
+ args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ assertNotBlocked(args);
+
+ // Second pointer (stylus pointer) goes down, from the same device
+ args = generateMotionArgs(1 /*downTime*/, 3 /*eventTime*/, MOVE, {{2, 3}, {11, 21}}, STYLUS);
+ // Keep source stylus, but make the tool type touch
+ args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ assertNotBlocked(args);
+}
+
+/**
+ * When there are two touch devices, stylus down should cancel all current touch streams.
+ */
+TEST_F(PreferStylusOverTouchTest, TouchFromTwoDevicesAndStylus) {
+ NotifyMotionArgs touch1Down =
+ generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertNotBlocked(touch1Down);
+
+ NotifyMotionArgs touch2Down =
+ generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{3, 4}}, TOUCHSCREEN);
+ touch2Down.deviceId = SECOND_TOUCH_DEVICE_ID;
+ assertNotBlocked(touch2Down);
+
+ NotifyMotionArgs stylusDown =
+ generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 30}}, STYLUS);
+ NotifyMotionArgs cancelArgs1 = touch1Down;
+ cancelArgs1.action = CANCEL;
+ cancelArgs1.flags |= AMOTION_EVENT_FLAG_CANCELED;
+ NotifyMotionArgs cancelArgs2 = touch2Down;
+ cancelArgs2.action = CANCEL;
+ cancelArgs2.flags |= AMOTION_EVENT_FLAG_CANCELED;
+ assertResponse(stylusDown, {cancelArgs1, cancelArgs2, stylusDown});
+}
+
+/**
+ * Touch should be canceled when stylus goes down. After the stylus lifts up, the touch from that
+ * device should continue to be canceled.
+ * If one of the devices is already canceled, it should remain canceled, but new touches from a
+ * different device should go through.
+ */
+TEST_F(PreferStylusOverTouchTest, AllTouchMustLiftAfterCanceledByStylus) {
+ // First device touches down
+ NotifyMotionArgs touch1Down =
+ generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertNotBlocked(touch1Down);
+
+ // Stylus goes down - touch should be canceled
+ NotifyMotionArgs stylusDown =
+ generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{10, 30}}, STYLUS);
+ NotifyMotionArgs cancelArgs1 = touch1Down;
+ cancelArgs1.action = CANCEL;
+ cancelArgs1.flags |= AMOTION_EVENT_FLAG_CANCELED;
+ assertResponse(stylusDown, {cancelArgs1, stylusDown});
+
+ // Stylus goes up
+ NotifyMotionArgs stylusUp =
+ generateMotionArgs(2 /*downTime*/, 3 /*eventTime*/, UP, {{10, 30}}, STYLUS);
+ assertNotBlocked(stylusUp);
+
+ // Touch from the first device remains blocked
+ NotifyMotionArgs touch1Move =
+ generateMotionArgs(1 /*downTime*/, 4 /*eventTime*/, MOVE, {{2, 3}}, TOUCHSCREEN);
+ assertDropped(touch1Move);
+
+ // Second touch goes down. It should not be blocked because stylus has already lifted.
+ NotifyMotionArgs touch2Down =
+ generateMotionArgs(5 /*downTime*/, 5 /*eventTime*/, DOWN, {{31, 32}}, TOUCHSCREEN);
+ touch2Down.deviceId = SECOND_TOUCH_DEVICE_ID;
+ assertNotBlocked(touch2Down);
+
+ // First device is lifted up. It's already been canceled, so the UP event should be dropped.
+ NotifyMotionArgs touch1Up =
+ generateMotionArgs(1 /*downTime*/, 6 /*eventTime*/, UP, {{2, 3}}, TOUCHSCREEN);
+ assertDropped(touch1Up);
+
+ // Touch from second device touch should continue to work
+ NotifyMotionArgs touch2Move =
+ generateMotionArgs(5 /*downTime*/, 7 /*eventTime*/, MOVE, {{32, 33}}, TOUCHSCREEN);
+ touch2Move.deviceId = SECOND_TOUCH_DEVICE_ID;
+ assertNotBlocked(touch2Move);
+
+ // Second touch lifts up
+ NotifyMotionArgs touch2Up =
+ generateMotionArgs(5 /*downTime*/, 8 /*eventTime*/, UP, {{32, 33}}, TOUCHSCREEN);
+ touch2Up.deviceId = SECOND_TOUCH_DEVICE_ID;
+ assertNotBlocked(touch2Up);
+
+ // Now that all touch has been lifted, new touch from either first or second device should work
+ NotifyMotionArgs touch3Down =
+ generateMotionArgs(9 /*downTime*/, 9 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertNotBlocked(touch3Down);
+
+ NotifyMotionArgs touch4Down =
+ generateMotionArgs(10 /*downTime*/, 10 /*eventTime*/, DOWN, {{100, 200}}, TOUCHSCREEN);
+ touch4Down.deviceId = SECOND_TOUCH_DEVICE_ID;
+ assertNotBlocked(touch4Down);
+}
+
+/**
+ * When we don't know that a specific device does both stylus and touch, and we only see touch
+ * pointers from it, we should treat it as a touch device. That means, the device events should be
+ * canceled when stylus from another device goes down. When we detect simultaneous touch and stylus
+ * from this device though, we should just pass this device through without canceling anything.
+ *
+ * In this test:
+ * 1. Start by touching down with device 1
+ * 2. Device 2 has stylus going down
+ * 3. Device 1 should be canceled.
+ * 4. When we add stylus pointers to the device 1, they should continue to be canceled.
+ * 5. Device 1 lifts up.
+ * 6. Subsequent events from device 1 should not be canceled even if stylus is down.
+ * 7. If a reset happens, and such device is no longer there, then we should
+ * Therefore, the device 1 is "ignored" and does not participate into "prefer stylus over touch"
+ * behaviour.
+ */
+TEST_F(PreferStylusOverTouchTest, MixedStylusAndTouchDeviceIsCanceledAtFirst) {
+ // Touch from device 1 goes down
+ NotifyMotionArgs touchDown =
+ generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ touchDown.source = STYLUS;
+ assertNotBlocked(touchDown);
+
+ // Stylus from device 2 goes down. Touch should be canceled.
+ NotifyMotionArgs args =
+ generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{10, 20}}, STYLUS);
+ NotifyMotionArgs cancelTouchArgs = touchDown;
+ cancelTouchArgs.action = CANCEL;
+ cancelTouchArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
+ assertResponse(args, {cancelTouchArgs, args});
+
+ // Introduce a stylus pointer into the device 1 stream. It should be ignored.
+ args = generateMotionArgs(1 /*downTime*/, 3 /*eventTime*/, POINTER_1_DOWN, {{1, 2}, {3, 4}},
+ TOUCHSCREEN);
+ args.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ args.source = STYLUS;
+ assertDropped(args);
+
+ // Lift up touch from the mixed touch/stylus device
+ args = generateMotionArgs(1 /*downTime*/, 4 /*eventTime*/, CANCEL, {{1, 2}, {3, 4}},
+ TOUCHSCREEN);
+ args.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ args.source = STYLUS;
+ assertDropped(args);
+
+ // Stylus from device 2 is still down. Since the device 1 is now identified as a mixed
+ // touch/stylus device, its events should go through, even if they are touch.
+ args = generateMotionArgs(5 /*downTime*/, 5 /*eventTime*/, DOWN, {{21, 22}}, TOUCHSCREEN);
+ touchDown.source = STYLUS;
+ assertResponse(args, {args});
+
+ // Reconfigure such that only the stylus device remains
+ InputDeviceInfo stylusDevice;
+ stylusDevice.initialize(STYLUS_DEVICE_ID, 1 /*generation*/, 1 /*controllerNumber*/,
+ {} /*identifier*/, "stylus device", false /*external*/,
+ false /*hasMic*/);
+ notifyInputDevicesChanged({stylusDevice});
+ // The touchscreen device was removed, so we no longer remember anything about it. We should
+ // again start blocking touch events from it.
+ args = generateMotionArgs(6 /*downTime*/, 6 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ args.source = STYLUS;
+ assertDropped(args);
+}
+
+/**
+ * If two styli are active at the same time, touch should be blocked until both of them are lifted.
+ * If one of them lifts, touch should continue to be blocked.
+ */
+TEST_F(PreferStylusOverTouchTest, TouchIsBlockedWhenTwoStyliAreUsed) {
+ NotifyMotionArgs args;
+
+ // First stylus is down
+ assertNotBlocked(generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{10, 30}}, STYLUS));
+
+ // Second stylus is down
+ args = generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{20, 40}}, STYLUS);
+ args.deviceId = SECOND_STYLUS_DEVICE_ID;
+ assertNotBlocked(args);
+
+ // Touch goes down. It should be ignored.
+ args = generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ // Lift the first stylus
+ args = generateMotionArgs(0 /*downTime*/, 3 /*eventTime*/, UP, {{10, 30}}, STYLUS);
+ assertNotBlocked(args);
+
+ // Touch should continue to be blocked
+ args = generateMotionArgs(2 /*downTime*/, 4 /*eventTime*/, UP, {{1, 2}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ // New touch should be blocked because second stylus is still down
+ args = generateMotionArgs(5 /*downTime*/, 5 /*eventTime*/, DOWN, {{5, 6}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ // Second stylus goes up
+ args = generateMotionArgs(1 /*downTime*/, 6 /*eventTime*/, UP, {{20, 40}}, STYLUS);
+ args.deviceId = SECOND_STYLUS_DEVICE_ID;
+ assertNotBlocked(args);
+
+ // Current touch gesture should continue to be blocked
+ // Touch should continue to be blocked
+ args = generateMotionArgs(5 /*downTime*/, 7 /*eventTime*/, UP, {{5, 6}}, TOUCHSCREEN);
+ assertDropped(args);
+
+ // Now that all styli were lifted, new touch should go through
+ args = generateMotionArgs(8 /*downTime*/, 8 /*eventTime*/, DOWN, {{7, 8}}, TOUCHSCREEN);
+ assertNotBlocked(args);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index a3220cc..e378096 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -490,6 +490,14 @@
&(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, DOWN, {{7, 8, 9}})));
}
+TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenStylusSourceWithFingerToolIsReceived) {
+ mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()});
+ NotifyMotionArgs args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}});
+ args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ args.source = AINPUT_SOURCE_STYLUS;
+ mBlocker->notifyMotion(&args);
+}
+
/**
* If input devices have changed, but the important device info that's used by the
* UnwantedInteractionBlocker has not changed, there should not be a reset.
@@ -511,6 +519,34 @@
&(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, MOVE, {{7, 8, 9}})));
}
+/**
+ * Send a touch event, and then a stylus event. Make sure that both work.
+ */
+TEST_F(UnwantedInteractionBlockerTest, StylusAfterTouchWorks) {
+ NotifyMotionArgs args;
+ mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()});
+ args = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+ mBlocker->notifyMotion(&args);
+ args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{4, 5, 6}});
+ mBlocker->notifyMotion(&args);
+ args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{4, 5, 6}});
+ mBlocker->notifyMotion(&args);
+
+ // Now touch down stylus
+ args = generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 20, 30}});
+ args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ args.source |= AINPUT_SOURCE_STYLUS;
+ mBlocker->notifyMotion(&args);
+ args = generateMotionArgs(3 /*downTime*/, 4 /*eventTime*/, MOVE, {{40, 50, 60}});
+ args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ args.source |= AINPUT_SOURCE_STYLUS;
+ mBlocker->notifyMotion(&args);
+ args = generateMotionArgs(3 /*downTime*/, 5 /*eventTime*/, UP, {{40, 50, 60}});
+ args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ args.source |= AINPUT_SOURCE_STYLUS;
+ mBlocker->notifyMotion(&args);
+}
+
using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest;
/**
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 7194db3..b18d204 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -257,11 +257,13 @@
bool isUidActive(uid_t uid);
- void onUidGone(uid_t uid, bool disabled);
- void onUidActive(uid_t uid);
- void onUidIdle(uid_t uid, bool disabled);
+ void onUidGone(uid_t uid, bool disabled) override;
+ void onUidActive(uid_t uid) override;
+ void onUidIdle(uid_t uid, bool disabled) override;
void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
- int64_t procStateSeq __unused, int32_t capability __unused) {}
+ int64_t procStateSeq __unused,
+ int32_t capability __unused) override {}
+ void onUidProcAdjChanged(uid_t uid __unused) override {}
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 7dd2331..6bad962 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -39,6 +39,7 @@
return 5;
case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+ case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES:
case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES:
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index bcae8d9..c5d7a60 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -1098,6 +1098,13 @@
}
}
+ if (s.what & layer_state_t::eDimmingEnabledChanged) {
+ if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
+ ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+ return false;
+ }
+ }
+
ALOGV("%s: true", __func__);
return true;
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index aefc014..516c3ef 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -23,6 +23,7 @@
"android.hardware.graphics.composer3-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
+ "android.hardware.power-V2-cpp",
"libbase",
"libcutils",
"libgui",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 283fe86..974f7c6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -207,6 +207,9 @@
// framerate of the layer as measured by LayerHistory
float fps;
+ // The dimming flag
+ bool dimmingEnabled{true};
+
virtual ~LayerFECompositionState();
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index ff7d430..6631a27 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -121,6 +121,7 @@
dumpVal(out, "isColorspaceAgnostic", isColorspaceAgnostic);
dumpVal(out, "dataspace", toString(dataspace), dataspace);
dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
+ dumpVal(out, "dimming enabled", dimmingEnabled);
dumpVal(out, "colorTransform", colorTransform);
out.append("\n");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 723593d..3289d55 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -324,9 +324,10 @@
// For hdr content, treat the white point as the display brightness - HDR content should not be
// boosted or dimmed.
+ // If the layer explicitly requests to disable dimming, then don't dim either.
if (isHdrDataspace(state.dataspace) ||
getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
- getOutput().getState().displayBrightnessNits == 0.f) {
+ getOutput().getState().displayBrightnessNits == 0.f || !layerFEState->dimmingEnabled) {
state.dimmingRatio = 1.f;
state.whitePointNits = getOutput().getState().displayBrightnessNits;
} else {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 8eb1946..ceee48c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -668,6 +668,13 @@
EXPECT_EQ(mOutputState.sdrWhitePointNits / mOutputState.displayBrightnessNits,
mOutputLayer.getState().dimmingRatio);
+ mLayerFEState.dimmingEnabled = false;
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+ EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+ EXPECT_EQ(1.f, mOutputLayer.getState().dimmingRatio);
+
+ // change dimmingEnabled back to true.
+ mLayerFEState.dimmingEnabled = true;
mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
mLayerFEState.isColorspaceAgnostic = false;
mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 2c4a300..d5d87b4 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -21,6 +21,7 @@
#include <string>
#include <unordered_map>
+#include <android-base/thread_annotations.h>
#include <android/native_window.h>
#include <binder/IBinder.h>
#include <gui/LayerState.h>
@@ -28,6 +29,7 @@
#include <renderengine/RenderEngine.h>
#include <system/window.h>
#include <ui/DisplayId.h>
+#include <ui/DisplayIdentification.h>
#include <ui/DisplayState.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
@@ -39,15 +41,11 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include "MainThreadGuard.h"
-
-#include <ui/DisplayIdentification.h>
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
-
#include "Scheduler/RefreshRateConfigs.h"
-
+#include "ThreadContext.h"
#include "TracedOrdinal.h"
namespace android {
@@ -99,9 +97,9 @@
void setLayerStack(ui::LayerStack);
void setDisplaySize(int width, int height);
void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
- void stageBrightness(float brightness) REQUIRES(SF_MAIN_THREAD);
- void persistBrightness(bool needsComposite) REQUIRES(SF_MAIN_THREAD);
- bool isBrightnessStale() const REQUIRES(SF_MAIN_THREAD);
+ void stageBrightness(float brightness) REQUIRES(kMainThreadContext);
+ void persistBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
+ bool isBrightnessStale() const REQUIRES(kMainThreadContext);
void setFlags(uint32_t flags);
ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
@@ -109,7 +107,7 @@
static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
- std::optional<float> getStagedBrightness() const REQUIRES(SF_MAIN_THREAD);
+ std::optional<float> getStagedBrightness() const REQUIRES(kMainThreadContext);
ui::Transform::RotationFlags getTransformHint() const;
const ui::Transform& getTransform() const;
const Rect& getLayerStackSpaceRect() const;
@@ -209,15 +207,15 @@
bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
- ActiveModeInfo getUpcomingActiveMode() const REQUIRES(SF_MAIN_THREAD) {
+ ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
return mUpcomingActiveMode;
}
- void setActiveMode(DisplayModeId) REQUIRES(SF_MAIN_THREAD);
+ void setActiveMode(DisplayModeId) REQUIRES(kMainThreadContext);
status_t initiateModeChange(const ActiveModeInfo&,
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline)
- REQUIRES(SF_MAIN_THREAD);
+ REQUIRES(kMainThreadContext);
// Return the immutable list of supported display modes. The HWC may report different modes
// after a hotplug reconnect event, in which case the DisplayDevice object will be recreated.
@@ -304,7 +302,7 @@
ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
TracedOrdinal<bool> mDesiredActiveModeChanged
GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
- ActiveModeInfo mUpcomingActiveMode GUARDED_BY(SF_MAIN_THREAD);
+ ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 44c086d..05f488b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -32,7 +32,6 @@
#include <utils/Trace.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/WorkDuration.h>
@@ -62,8 +61,6 @@
using scheduler::OneShotTimer;
-class AidlPowerHalWrapper;
-
PowerAdvisor::~PowerAdvisor() = default;
namespace {
@@ -294,259 +291,232 @@
const sp<V1_3::IPower> mPowerHal = nullptr;
};
-class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
-public:
- AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
- auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering);
- if (!ret.isOk()) {
- mHasExpensiveRendering = false;
- }
-
- ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT,
- &mHasDisplayUpdateImminent);
- if (!ret.isOk()) {
- mHasDisplayUpdateImminent = false;
- }
-
- mSupportsPowerHint = checkPowerHintSessionSupported();
+AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
+ auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering);
+ if (!ret.isOk()) {
+ mHasExpensiveRendering = false;
}
- ~AidlPowerHalWrapper() override {
- if (mPowerHintSession != nullptr) {
- mPowerHintSession->close();
- mPowerHintSession = nullptr;
- }
- };
-
- static std::unique_ptr<HalWrapper> connect() {
- // This only waits if the service is actually declared
- sp<IPower> powerHal = waitForVintfService<IPower>();
- if (powerHal == nullptr) {
- return nullptr;
- }
- ALOGI("Loaded AIDL Power HAL service");
-
- return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
+ ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT, &mHasDisplayUpdateImminent);
+ if (!ret.isOk()) {
+ mHasDisplayUpdateImminent = false;
}
- bool setExpensiveRendering(bool enabled) override {
- ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F");
- if (!mHasExpensiveRendering) {
- ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it");
- return true;
- }
+ mSupportsPowerHint = checkPowerHintSessionSupported();
+}
- auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
- if (ret.isOk()) {
- traceExpensiveRendering(enabled);
- }
- return ret.isOk();
+AidlPowerHalWrapper::~AidlPowerHalWrapper() {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
}
-
- bool notifyDisplayUpdateImminent() override {
- ALOGV("AIDL notifyDisplayUpdateImminent");
- if (!mHasDisplayUpdateImminent) {
- ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
- return true;
- }
-
- auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
- return ret.isOk();
- }
-
- // only version 2+ of the aidl supports power hint sessions, hidl has no support
- bool supportsPowerHintSession() override { return mSupportsPowerHint; }
-
- bool checkPowerHintSessionSupported() {
- int64_t unused;
- // Try to get preferred rate to determine if hint sessions are supported
- // We check for isOk not EX_UNSUPPORTED_OPERATION to lump other errors
- return mPowerHal->getHintSessionPreferredRate(&unused).isOk();
- }
-
- bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; }
-
- void closePowerHintSession() {
- if (mPowerHintSession != nullptr) {
- mPowerHintSession->close();
- mPowerHintSession = nullptr;
- }
- }
-
- void restartPowerHintSession() {
- closePowerHintSession();
- startPowerHintSession();
- }
-
- void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override {
- if (threadIds != mPowerHintThreadIds) {
- mPowerHintThreadIds = threadIds;
- if (isPowerHintSessionRunning()) {
- restartPowerHintSession();
- }
- }
- }
-
- bool startPowerHintSession() override {
- if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) {
- ALOGV("Cannot start power hint session, skipping");
- return false;
- }
- auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
- mPowerHintThreadIds, mTargetDuration,
- &mPowerHintSession);
- if (!ret.isOk()) {
- ALOGW("Failed to start power hint session with error: %s",
- ret.exceptionToString(ret.exceptionCode()).c_str());
- } else {
- mLastTargetDurationSent = mTargetDuration;
- }
- return isPowerHintSessionRunning();
- }
-
- bool shouldSetTargetDuration(int64_t targetDurationNanos) {
- // report if the change in target from our last submission to now exceeds the threshold
- return abs(1.0 -
- static_cast<double>(mLastTargetDurationSent) /
- static_cast<double>(targetDurationNanos)) >=
- kAllowedTargetDeviationPercent;
- }
-
- void setTargetWorkDuration(int64_t targetDurationNanos) override {
- ATRACE_CALL();
- mTargetDuration = targetDurationNanos;
- if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
- if (!sNormalizeTarget && shouldSetTargetDuration(targetDurationNanos) &&
- isPowerHintSessionRunning()) {
- if (mLastActualDurationSent.has_value()) {
- // update the error term here since we are actually sending an update to powerhal
- if (sTraceHintSessionData)
- ATRACE_INT64("Target error term",
- targetDurationNanos - *mLastActualDurationSent);
- }
- ALOGV("Sending target time: %lld ns", static_cast<long long>(targetDurationNanos));
- mLastTargetDurationSent = targetDurationNanos;
- auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
- if (!ret.isOk()) {
- ALOGW("Failed to set power hint target work duration with error: %s",
- ret.exceptionMessage().c_str());
- mShouldReconnectHal = true;
- }
- }
- }
-
- bool shouldReportActualDurationsNow() {
- // report if we have never reported before or are approaching a stale session
- if (!mLastActualDurationSent.has_value() ||
- (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
- return true;
- }
-
- if (!mActualDuration.has_value()) {
- return false;
- }
-
- // duration of most recent timing
- const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
- // duration of the last timing actually reported to the powerhal
- const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
-
- // report if the change in duration from then to now exceeds the threshold
- return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
- kAllowedActualDeviationPercent;
- }
-
- void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override {
- ATRACE_CALL();
-
- if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
- ALOGV("Failed to send actual work duration, skipping");
- return;
- }
-
- WorkDuration duration;
- duration.durationNanos = actualDurationNanos;
- mActualDuration = actualDurationNanos;
-
- // normalize the sent values to a pre-set target
- if (sNormalizeTarget) {
- duration.durationNanos += mLastTargetDurationSent - mTargetDuration;
- }
- duration.timeStampNanos = timeStampNanos;
- mPowerHintQueue.push_back(duration);
-
- long long targetNsec = mTargetDuration;
- long long durationNsec = actualDurationNanos;
-
- if (sTraceHintSessionData) {
- ATRACE_INT64("Measured duration", durationNsec);
- ATRACE_INT64("Target error term", targetNsec - durationNsec);
- }
-
- ALOGV("Sending actual work duration of: %lld on target: %lld with error: %lld",
- durationNsec, targetNsec, targetNsec - durationNsec);
-
- // This rate limiter queues similar duration reports to the powerhal into
- // batches to avoid excessive binder calls. The criteria to send a given batch
- // are outlined in shouldReportActualDurationsNow()
- if (shouldReportActualDurationsNow()) {
- ALOGV("Sending hint update batch");
- mLastActualReportTimestamp = systemTime();
- auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
- if (!ret.isOk()) {
- ALOGW("Failed to report actual work durations with error: %s",
- ret.exceptionMessage().c_str());
- mShouldReconnectHal = true;
- }
- mPowerHintQueue.clear();
- // we save the non-normalized value here to detect % changes
- mLastActualDurationSent = actualDurationNanos;
- }
- }
-
- bool shouldReconnectHAL() override { return mShouldReconnectHal; }
-
- std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }
-
- std::optional<int64_t> getTargetWorkDuration() override { return mTargetDuration; }
-
-private:
- const sp<IPower> mPowerHal = nullptr;
- bool mHasExpensiveRendering = false;
- bool mHasDisplayUpdateImminent = false;
- // Used to indicate an error state and need for reconstruction
- bool mShouldReconnectHal = false;
- // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
- sp<IPowerHintSession> mPowerHintSession = nullptr;
- // Queue of actual durations saved to report
- std::vector<WorkDuration> mPowerHintQueue;
- // The latest un-normalized values we have received for target and actual
- int64_t mTargetDuration = kDefaultTarget.count();
- std::optional<int64_t> mActualDuration;
- // The list of thread ids, stored so we can restart the session from this class if needed
- std::vector<int32_t> mPowerHintThreadIds;
- bool mSupportsPowerHint;
- // Keep track of the last messages sent for rate limiter change detection
- std::optional<int64_t> mLastActualDurationSent;
- // timestamp of the last report we sent, used to avoid stale sessions
- int64_t mLastActualReportTimestamp = 0;
- int64_t mLastTargetDurationSent = kDefaultTarget.count();
- // Whether to normalize all the actual values as error terms relative to a constant target
- // This saves a binder call by not setting the target, and should not affect the pid values
- static const bool sNormalizeTarget;
- // Whether we should emit ATRACE_INT data for hint sessions
- static const bool sTraceHintSessionData;
- // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
- static constexpr double kAllowedActualDeviationPercent = 0.1;
- // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
- static constexpr double kAllowedTargetDeviationPercent = 0.05;
- // Target used for init and normalization, the actual value does not really matter
- static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
- // Amount of time after the last message was sent before the session goes stale
- // actually 100ms but we use 80 here to ideally avoid going stale
- static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
};
+std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() {
+ // This only waits if the service is actually declared
+ sp<IPower> powerHal = waitForVintfService<IPower>();
+ if (powerHal == nullptr) {
+ return nullptr;
+ }
+ ALOGI("Loaded AIDL Power HAL service");
+
+ return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
+}
+
+bool AidlPowerHalWrapper::setExpensiveRendering(bool enabled) {
+ ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F");
+ if (!mHasExpensiveRendering) {
+ ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it");
+ return true;
+ }
+
+ auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
+ if (ret.isOk()) {
+ traceExpensiveRendering(enabled);
+ }
+ return ret.isOk();
+}
+
+bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() {
+ ALOGV("AIDL notifyDisplayUpdateImminent");
+ if (!mHasDisplayUpdateImminent) {
+ ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
+ return true;
+ }
+
+ auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
+ return ret.isOk();
+}
+
+// only version 2+ of the aidl supports power hint sessions, hidl has no support
+bool AidlPowerHalWrapper::supportsPowerHintSession() {
+ return mSupportsPowerHint;
+}
+
+bool AidlPowerHalWrapper::checkPowerHintSessionSupported() {
+ int64_t unused;
+ // Try to get preferred rate to determine if hint sessions are supported
+ // We check for isOk not EX_UNSUPPORTED_OPERATION to lump together errors
+ return mPowerHal->getHintSessionPreferredRate(&unused).isOk();
+}
+
+bool AidlPowerHalWrapper::isPowerHintSessionRunning() {
+ return mPowerHintSession != nullptr;
+}
+
+void AidlPowerHalWrapper::closePowerHintSession() {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
+ }
+}
+
+void AidlPowerHalWrapper::restartPowerHintSession() {
+ closePowerHintSession();
+ startPowerHintSession();
+}
+
+void AidlPowerHalWrapper::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
+ if (threadIds != mPowerHintThreadIds) {
+ mPowerHintThreadIds = threadIds;
+ if (isPowerHintSessionRunning()) {
+ restartPowerHintSession();
+ }
+ }
+}
+
+bool AidlPowerHalWrapper::startPowerHintSession() {
+ if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) {
+ ALOGV("Cannot start power hint session, skipping");
+ return false;
+ }
+ auto ret =
+ mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
+ mPowerHintThreadIds, mTargetDuration, &mPowerHintSession);
+ if (!ret.isOk()) {
+ ALOGW("Failed to start power hint session with error: %s",
+ ret.exceptionToString(ret.exceptionCode()).c_str());
+ } else {
+ mLastTargetDurationSent = mTargetDuration;
+ }
+ return isPowerHintSessionRunning();
+}
+
+bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) {
+ if (targetDurationNanos <= 0) {
+ return false;
+ }
+ // report if the change in target from our last submission to now exceeds the threshold
+ return abs(1.0 -
+ static_cast<double>(mLastTargetDurationSent) /
+ static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent;
+}
+
+void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) {
+ ATRACE_CALL();
+ mTargetDuration = targetDurationNanos;
+ if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
+ if (!sNormalizeTarget && isPowerHintSessionRunning() &&
+ shouldSetTargetDuration(targetDurationNanos)) {
+ if (mLastActualDurationSent.has_value()) {
+ // update the error term here since we are actually sending an update to powerhal
+ if (sTraceHintSessionData)
+ ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent);
+ }
+ ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos);
+ mLastTargetDurationSent = targetDurationNanos;
+ auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
+ if (!ret.isOk()) {
+ ALOGW("Failed to set power hint target work duration with error: %s",
+ ret.exceptionMessage().c_str());
+ mShouldReconnectHal = true;
+ }
+ }
+}
+
+bool AidlPowerHalWrapper::shouldReportActualDurationsNow() {
+ // report if we have never reported before or are approaching a stale session
+ if (!mLastActualDurationSent.has_value() ||
+ (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
+ return true;
+ }
+
+ if (!mActualDuration.has_value()) {
+ return false;
+ }
+
+ // duration of most recent timing
+ const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
+ // duration of the last timing actually reported to the powerhal
+ const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
+
+ // report if the change in duration from then to now exceeds the threshold
+ return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
+ kAllowedActualDeviationPercent;
+}
+
+void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos,
+ nsecs_t timeStampNanos) {
+ ATRACE_CALL();
+
+ if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+ ALOGV("Failed to send actual work duration, skipping");
+ return;
+ }
+
+ WorkDuration duration;
+ duration.durationNanos = actualDurationNanos;
+ mActualDuration = actualDurationNanos;
+
+ // normalize the sent values to a pre-set target
+ if (sNormalizeTarget) {
+ duration.durationNanos += mLastTargetDurationSent - mTargetDuration;
+ }
+ duration.timeStampNanos = timeStampNanos;
+ mPowerHintQueue.push_back(duration);
+
+ nsecs_t targetNsec = mTargetDuration;
+ nsecs_t durationNsec = actualDurationNanos;
+
+ if (sTraceHintSessionData) {
+ ATRACE_INT64("Measured duration", durationNsec);
+ ATRACE_INT64("Target error term", targetNsec - durationNsec);
+ }
+
+ ALOGV("Sending actual work duration of: %" PRId64 " on target: %" PRId64
+ " with error: %" PRId64,
+ durationNsec, targetNsec, targetNsec - durationNsec);
+
+ // This rate limiter queues similar duration reports to the powerhal into
+ // batches to avoid excessive binder calls. The criteria to send a given batch
+ // are outlined in shouldReportActualDurationsNow()
+ if (shouldReportActualDurationsNow()) {
+ ALOGV("Sending hint update batch");
+ mLastActualReportTimestamp = systemTime();
+ auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
+ if (!ret.isOk()) {
+ ALOGW("Failed to report actual work durations with error: %s",
+ ret.exceptionMessage().c_str());
+ mShouldReconnectHal = true;
+ }
+ mPowerHintQueue.clear();
+ // we save the non-normalized value here to detect % changes
+ mLastActualDurationSent = actualDurationNanos;
+ }
+}
+
+bool AidlPowerHalWrapper::shouldReconnectHAL() {
+ return mShouldReconnectHal;
+}
+
+std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() {
+ return mPowerHintThreadIds;
+}
+
+std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() {
+ return mTargetDuration;
+}
+
const bool AidlPowerHalWrapper::sTraceHintSessionData =
base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 0db56aa..3f47ffd 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -22,6 +22,7 @@
#include <utils/Mutex.h>
+#include <android/hardware/power/IPower.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
@@ -118,6 +119,69 @@
scheduler::OneShotTimer mScreenUpdateTimer;
};
+class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
+public:
+ explicit AidlPowerHalWrapper(sp<hardware::power::IPower> powerHal);
+ ~AidlPowerHalWrapper() override;
+
+ static std::unique_ptr<HalWrapper> connect();
+
+ bool setExpensiveRendering(bool enabled) override;
+ bool notifyDisplayUpdateImminent() override;
+ bool supportsPowerHintSession() override;
+ bool isPowerHintSessionRunning() override;
+ void restartPowerHintSession() override;
+ void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
+ bool startPowerHintSession() override;
+ void setTargetWorkDuration(int64_t targetDurationNanos) override;
+ void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override;
+ bool shouldReconnectHAL() override;
+ std::vector<int32_t> getPowerHintSessionThreadIds() override;
+ std::optional<int64_t> getTargetWorkDuration() override;
+
+private:
+ bool checkPowerHintSessionSupported();
+ void closePowerHintSession();
+ bool shouldReportActualDurationsNow();
+ bool shouldSetTargetDuration(int64_t targetDurationNanos);
+
+ const sp<hardware::power::IPower> mPowerHal = nullptr;
+ bool mHasExpensiveRendering = false;
+ bool mHasDisplayUpdateImminent = false;
+ // Used to indicate an error state and need for reconstruction
+ bool mShouldReconnectHal = false;
+ // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+ sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr;
+ // Queue of actual durations saved to report
+ std::vector<hardware::power::WorkDuration> mPowerHintQueue;
+ // The latest un-normalized values we have received for target and actual
+ int64_t mTargetDuration = kDefaultTarget.count();
+ std::optional<int64_t> mActualDuration;
+ // The list of thread ids, stored so we can restart the session from this class if needed
+ std::vector<int32_t> mPowerHintThreadIds;
+ bool mSupportsPowerHint;
+ // Keep track of the last messages sent for rate limiter change detection
+ std::optional<int64_t> mLastActualDurationSent;
+ // timestamp of the last report we sent, used to avoid stale sessions
+ int64_t mLastActualReportTimestamp = 0;
+ int64_t mLastTargetDurationSent = kDefaultTarget.count();
+ // Whether to normalize all the actual values as error terms relative to a constant target
+ // This saves a binder call by not setting the target, and should not affect the pid values
+ static const bool sNormalizeTarget;
+ // Whether we should emit ATRACE_INT data for hint sessions
+ static const bool sTraceHintSessionData;
+
+ // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
+ static constexpr double kAllowedActualDeviationPercent = 0.1;
+ // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
+ static constexpr double kAllowedTargetDeviationPercent = 0.05;
+ // Target used for init and normalization, the actual value does not really matter
+ static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
+ // Amount of time after the last message was sent before the session goes stale
+ // actually 100ms but we use 80 here to ideally avoid going stale
+ static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
+};
+
} // namespace impl
} // namespace Hwc2
} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index fa5a1c8..3d00b90 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -37,6 +37,7 @@
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <ftl/enum.h>
+#include <ftl/fake_guard.h>
#include <gui/BufferItem.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
@@ -139,6 +140,7 @@
mDrawingState.destinationFrame.makeInvalid();
mDrawingState.isTrustedOverlay = false;
mDrawingState.dropInputMode = gui::DropInputMode::NONE;
+ mDrawingState.dimmingEnabled = true;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -477,6 +479,7 @@
compositionState->colorTransformIsIdentity = !hasColorTransform();
compositionState->surfaceDamage = surfaceDamageRegion;
compositionState->hasProtectedContent = isProtected();
+ compositionState->dimmingEnabled = isDimmingEnabled();
const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
@@ -1038,6 +1041,16 @@
return true;
}
+bool Layer::setDimmingEnabled(const bool dimmingEnabled) {
+ if (mDrawingState.dimmingEnabled == dimmingEnabled) return false;
+
+ mDrawingState.sequence++;
+ mDrawingState.dimmingEnabled = dimmingEnabled;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setFrameRateSelectionPriority(int32_t priority) {
if (mDrawingState.frameRateSelectionPriority == priority) return false;
mDrawingState.frameRateSelectionPriority = priority;
@@ -2045,10 +2058,10 @@
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
+ ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
+
// Only populate for the primary display.
- UnnecessaryLock assumeLocked(mFlinger->mStateLock); // called from the main thread.
- const auto display = mFlinger->getDefaultDisplayDeviceLocked();
- if (display) {
+ if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
const auto compositionType = getCompositionType(*display);
layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 846460d..565a6ff 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -281,6 +281,8 @@
gui::DropInputMode dropInputMode;
bool autoRefresh = false;
+
+ bool dimmingEnabled = true;
};
/*
@@ -411,6 +413,7 @@
virtual mat4 getColorTransform() const;
virtual bool hasColorTransform() const;
virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
+ virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; };
// Used only to set BufferStateLayer state
virtual bool setTransform(uint32_t /*transform*/) { return false; };
@@ -437,6 +440,7 @@
}
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
+ virtual bool setDimmingEnabled(const bool dimmingEnabled);
virtual bool setFrameRateSelectionPriority(int32_t priority);
virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
virtual void setAutoRefresh(bool /* autoRefresh */) {}
diff --git a/services/surfaceflinger/MainThreadGuard.h b/services/surfaceflinger/MainThreadGuard.h
deleted file mode 100644
index c1aa118..0000000
--- a/services/surfaceflinger/MainThreadGuard.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/Mutex.h>
-
-namespace android {
-namespace {
-
-// Helps to ensure that some functions runs on SF's main thread by using the
-// clang thread safety annotations.
-class CAPABILITY("mutex") MainThreadGuard {
-} SF_MAIN_THREAD;
-
-struct SCOPED_CAPABILITY MainThreadScopedGuard {
-public:
- explicit MainThreadScopedGuard(MainThreadGuard& mutex) ACQUIRE(mutex) {}
- ~MainThreadScopedGuard() RELEASE() {}
-};
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/MutexUtils.h b/services/surfaceflinger/MutexUtils.h
new file mode 100644
index 0000000..f8be6f3
--- /dev/null
+++ b/services/surfaceflinger/MutexUtils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <log/log.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+struct SCOPED_CAPABILITY ConditionalLock {
+ ConditionalLock(Mutex& mutex, bool lock) ACQUIRE(mutex) : mutex(mutex), lock(lock) {
+ if (lock) mutex.lock();
+ }
+
+ ~ConditionalLock() RELEASE() {
+ if (lock) mutex.unlock();
+ }
+
+ Mutex& mutex;
+ const bool lock;
+};
+
+struct SCOPED_CAPABILITY TimedLock {
+ TimedLock(Mutex& mutex, nsecs_t timeout, const char* whence) ACQUIRE(mutex)
+ : mutex(mutex), status(mutex.timedLock(timeout)) {
+ ALOGE_IF(!locked(), "%s timed out locking: %s (%d)", whence, strerror(-status), status);
+ }
+
+ ~TimedLock() RELEASE() {
+ if (locked()) mutex.unlock();
+ }
+
+ bool locked() const { return status == NO_ERROR; }
+
+ Mutex& mutex;
+ const status_t status;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9f5a7df..5adc177 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -24,7 +24,10 @@
#include "SurfaceFlinger.h"
+#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android/configuration.h>
#include <android/gui/IDisplayEventConnection.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
@@ -49,6 +52,7 @@
#include <configstore/Utils.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
+#include <ftl/fake_guard.h>
#include <ftl/future.h>
#include <ftl/small_map.h>
#include <gui/BufferQueue.h>
@@ -123,6 +127,7 @@
#include "LayerRenderArea.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
+#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
@@ -138,49 +143,26 @@
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
#include "WindowInfosListenerInvoker.h"
-#include "android-base/parseint.h"
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
-#define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
-
-// Note: The parentheses around `expr` are needed to deduce an lvalue or rvalue reference.
-#define ON_MAIN_THREAD(expr) \
- [&]() -> decltype(auto) { \
- LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
- UnnecessaryLock lock(mStateLock); \
- return (expr); \
- }()
-
-#define MAIN_THREAD_GUARD(expr) \
- [&]() -> decltype(auto) { \
- LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
- MainThreadScopedGuard lock(SF_MAIN_THREAD); \
- return (expr); \
- }()
-
#undef NO_THREAD_SAFETY_ANALYSIS
#define NO_THREAD_SAFETY_ANALYSIS \
- _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
-
-using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
-using aidl::android::hardware::graphics::composer3::Capability;
-using aidl::android::hardware::graphics::composer3::DisplayCapability;
-using KernelIdleTimerController =
- ::android::scheduler::RefreshRateConfigs::KernelIdleTimerController;
+ _Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"")
namespace android {
using namespace std::string_literals;
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using namespace android::sysprop;
+using namespace hardware::configstore;
+using namespace hardware::configstore::V1_0;
+using namespace sysprop;
-using android::hardware::power::Boost;
+using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
+using aidl::android::hardware::graphics::composer3::Capability;
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
+
using base::StringAppendF;
using gui::DisplayInfo;
using gui::IDisplayEventConnection;
@@ -191,6 +173,8 @@
using ui::DisplayPrimaries;
using ui::RenderIntent;
+using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController;
+
namespace hal = android::hardware::graphics::composer::hal;
namespace {
@@ -222,38 +206,6 @@
#pragma clang diagnostic pop
-template <typename Mutex>
-struct SCOPED_CAPABILITY ConditionalLockGuard {
- ConditionalLockGuard(Mutex& mutex, bool lock) ACQUIRE(mutex) : mutex(mutex), lock(lock) {
- if (lock) mutex.lock();
- }
-
- ~ConditionalLockGuard() RELEASE() {
- if (lock) mutex.unlock();
- }
-
- Mutex& mutex;
- const bool lock;
-};
-
-using ConditionalLock = ConditionalLockGuard<Mutex>;
-
-struct SCOPED_CAPABILITY TimedLock {
- TimedLock(Mutex& mutex, nsecs_t timeout, const char* whence) ACQUIRE(mutex)
- : mutex(mutex), status(mutex.timedLock(timeout)) {
- ALOGE_IF(!locked(), "%s timed out locking: %s (%d)", whence, strerror(-status), status);
- }
-
- ~TimedLock() RELEASE() {
- if (locked()) mutex.unlock();
- }
-
- bool locked() const { return status == NO_ERROR; }
-
- Mutex& mutex;
- const status_t status;
-};
-
// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
@@ -729,13 +681,15 @@
if (renderEngineTid.has_value()) {
tidList.emplace_back(*renderEngineTid);
}
- mPowerAdvisor.startPowerHintSession(tidList);
+ if (!mPowerAdvisor.startPowerHintSession(tidList)) {
+ ALOGW("Cannot start power hint session");
+ }
}
mBootStage = BootStage::FINISHED;
if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
- ON_MAIN_THREAD(enableRefreshRateOverlay(true));
+ FTL_FAKE_GUARD(mStateLock, enableRefreshRateOverlay(true));
}
}));
}
@@ -936,8 +890,9 @@
FrameEvent::DEQUEUE_READY,
FrameEvent::RELEASE,
};
- ConditionalLock _l(mStateLock,
- std::this_thread::get_id() != mMainThreadId);
+
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+
if (!getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
outSupported->push_back(FrameEvent::DISPLAY_PRESENT);
}
@@ -1040,13 +995,8 @@
outMode.resolution = ui::Size(width, height);
- if (mEmulatedDisplayDensity) {
- outMode.xDpi = mEmulatedDisplayDensity;
- outMode.yDpi = mEmulatedDisplayDensity;
- } else {
- outMode.xDpi = xDpi;
- outMode.yDpi = yDpi;
- }
+ outMode.xDpi = xDpi;
+ outMode.yDpi = yDpi;
const nsecs_t period = mode->getVsyncPeriod();
outMode.refreshRate = Fps::fromPeriodNsecs(period).getValue();
@@ -1142,7 +1092,7 @@
}
auto future = mScheduler->schedule([=]() -> status_t {
- const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set allowed display modes for invalid display token %p",
displayToken.get());
@@ -1183,7 +1133,9 @@
return;
}
- const auto upcomingModeInfo = MAIN_THREAD_GUARD(display->getUpcomingActiveMode());
+ const auto upcomingModeInfo =
+ FTL_FAKE_GUARD(kMainThreadContext, display->getUpcomingActiveMode());
+
if (!upcomingModeInfo.mode) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
@@ -1202,9 +1154,8 @@
return;
}
- // We just created this display so we can call even if we are not on
- // the main thread
- MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+ // We just created this display so we can call even if we are not on the main thread.
+ ftl::FakeGuard guard(kMainThreadContext);
display->setActiveMode(upcomingModeInfo.mode->getId());
const Fps refreshRate = upcomingModeInfo.mode->getFps();
@@ -1289,8 +1240,10 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- const auto status = MAIN_THREAD_GUARD(
- display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline));
+ const auto status = FTL_FAKE_GUARD(kMainThreadContext,
+ display->initiateModeChange(*desiredActiveMode,
+ constraints, &outTimeline));
+
if (status != NO_ERROR) {
// initiateModeChange may fail if a hotplug event is just about
// to be sent. We just log the error in this case.
@@ -1323,7 +1276,7 @@
}
void SurfaceFlinger::disableExpensiveRendering() {
- auto future = mScheduler->schedule([=]() MAIN_THREAD {
+ auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
ATRACE_CALL();
if (mPowerAdvisor.isUsingExpensiveRendering()) {
for (const auto& [_, display] : mDisplays) {
@@ -1372,7 +1325,7 @@
return BAD_VALUE;
}
- auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+ auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
@@ -1407,17 +1360,17 @@
}
status_t SurfaceFlinger::getBootDisplayModeSupport(bool* outSupport) const {
- auto future = mScheduler->schedule([=]() MAIN_THREAD mutable -> status_t {
- *outSupport = getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG);
- return NO_ERROR;
- });
- return future.get();
+ auto future = mScheduler->schedule(
+ [this] { return getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG); });
+
+ *outSupport = future.get();
+ return NO_ERROR;
}
status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken,
ui::DisplayModeId modeId) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+ auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("%s: Invalid display token %p", whence, displayToken.get());
@@ -1444,7 +1397,7 @@
status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+ auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().clearBootDisplayMode(*displayId);
} else {
@@ -1457,7 +1410,7 @@
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
+ static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
getHwComposer().setAutoLowLatencyMode(*displayId, on);
} else {
@@ -1468,7 +1421,7 @@
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
+ static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
getHwComposer().setContentType(*displayId, type);
@@ -1533,7 +1486,7 @@
bool enable, uint8_t componentMask,
uint64_t maxFrames) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+ auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
componentMask, maxFrames);
@@ -1610,7 +1563,7 @@
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
outLayers->clear();
auto future = mScheduler->schedule([=] {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
mDrawingState.traverseInZOrder([&](Layer* layer) {
outLayers->push_back(layer->getLayerDebugInfo(display.get()));
});
@@ -1722,7 +1675,7 @@
}
const char* const whence = __func__;
- return ftl::chain(mScheduler->schedule([=]() MAIN_THREAD {
+ return ftl::chain(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
if (const auto display = getDisplayDeviceLocked(displayToken)) {
const bool supportsDisplayBrightnessCommand =
getHwComposer().getComposer()->isSupported(
@@ -1736,7 +1689,9 @@
compositionDisplay->editState().displayBrightnessNits;
compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
brightness.displayBrightnessNits);
- MAIN_THREAD_GUARD(display->stageBrightness(brightness.displayBrightness));
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->stageBrightness(brightness.displayBrightness));
+
if (brightness.sdrWhitePointNits / brightness.displayBrightnessNits !=
currentDimmingRatio) {
scheduleComposite(FrameHint::kNone);
@@ -1806,6 +1761,7 @@
}
status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
+ using hardware::power::Boost;
Boost powerBoost = static_cast<Boost>(boostId);
if (powerBoost == Boost::INTERACTION) {
@@ -1966,7 +1922,7 @@
ATRACE_CALL();
// On main thread to avoid race conditions with display power state.
- static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
+ static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
if (const auto display = getDefaultDisplayDeviceLocked();
@@ -2014,8 +1970,8 @@
: stats.vsyncTime + stats.vsyncPeriod;
}
-bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) {
- MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
+bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
+ FTL_FAKE_GUARD(kMainThreadContext) {
// we set this once at the beginning of commit to ensure consistency throughout the whole frame
mPowerHintSessionData.sessionEnabled = mPowerAdvisor.usePowerHintSession();
if (mPowerHintSessionData.sessionEnabled) {
@@ -2181,20 +2137,21 @@
mLayerTracing.notify(mVisibleRegionsDirty, frameTime);
}
- MAIN_THREAD_GUARD(persistDisplayBrightness(mustComposite));
+ persistDisplayBrightness(mustComposite);
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) {
+void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId)
+ FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
- MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
+
if (mPowerHintSessionData.sessionEnabled) {
mPowerHintSessionData.compositeStart = systemTime();
}
compositionengine::CompositionRefreshArgs refreshArgs;
- const auto& displays = ON_MAIN_THREAD(mDisplays);
+ const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
for (const auto& [_, display] : displays) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
@@ -2432,7 +2389,7 @@
ATRACE_CALL();
ALOGV("postComposition");
- const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get();
+ const auto* display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
getBE().mGlCompositionDoneTimeline.updateSignalTimes();
std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
@@ -2632,44 +2589,37 @@
}
FloatRect SurfaceFlinger::getMaxDisplayBounds() {
- // Find the largest width and height among all the displays.
- int32_t maxDisplayWidth = 0;
- int32_t maxDisplayHeight = 0;
+ const ui::Size maxSize = [this] {
+ ftl::FakeGuard guard(mStateLock);
- // If there are no displays, set a valid display bounds so we can still compute a valid layer
- // bounds.
- if (ON_MAIN_THREAD(mDisplays.size()) == 0) {
- maxDisplayWidth = maxDisplayHeight = 5000;
- }
+ // The LayerTraceGenerator tool runs without displays.
+ if (mDisplays.empty()) return ui::Size{5000, 5000};
- for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
- const auto& displayDevice = pair.second;
- int32_t width = displayDevice->getWidth();
- int32_t height = displayDevice->getHeight();
- if (width > maxDisplayWidth) {
- maxDisplayWidth = width;
- }
- if (height > maxDisplayHeight) {
- maxDisplayHeight = height;
- }
- }
+ return std::accumulate(mDisplays.begin(), mDisplays.end(), ui::kEmptySize,
+ [](ui::Size size, const auto& pair) -> ui::Size {
+ const auto& display = pair.second;
+ return {std::max(size.getWidth(), display->getWidth()),
+ std::max(size.getHeight(), display->getHeight())};
+ });
+ }();
// Ignore display bounds for now since they will be computed later. Use a large Rect bound
// to ensure it's bigger than an actual display will be.
- FloatRect maxBounds = FloatRect(-maxDisplayWidth * 10, -maxDisplayHeight * 10,
- maxDisplayWidth * 10, maxDisplayHeight * 10);
- return maxBounds;
+ const float xMax = maxSize.getWidth() * 10.f;
+ const float yMax = maxSize.getHeight() * 10.f;
+
+ return {-xMax, -yMax, xMax, yMax};
}
void SurfaceFlinger::computeLayerBounds() {
- FloatRect maxBounds = getMaxDisplayBounds();
+ const FloatRect maxBounds = getMaxDisplayBounds();
for (const auto& layer : mDrawingState.layersSortedByZ) {
layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
}
}
void SurfaceFlinger::postFrame() {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (display && getHwComposer().isConnected(display->getPhysicalId())) {
uint32_t flipCount = display->getPageFlipCount();
if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
@@ -2923,7 +2873,8 @@
RenderIntent::COLORIMETRIC,
Dataspace::UNKNOWN});
if (!state.isVirtual()) {
- MAIN_THREAD_GUARD(display->setActiveMode(state.physical->activeMode->getId()));
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->setActiveMode(state.physical->activeMode->getId()));
display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
@@ -3293,7 +3244,7 @@
return;
}
- for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+ for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
if (const auto brightness = display->getStagedBrightness(); brightness) {
if (!needsComposite) {
const status_t error =
@@ -3316,7 +3267,7 @@
std::vector<DisplayInfo>& outDisplayInfos) {
ftl::SmallMap<ui::LayerStack, DisplayDevice::InputInfo, 4> displayInputInfos;
- for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+ for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
const auto layerStack = display->getLayerStack();
const auto info = display->getInputInfo();
@@ -3361,7 +3312,7 @@
void SurfaceFlinger::updateCursorAsync() {
compositionengine::CompositionRefreshArgs refreshArgs;
- for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+ for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
if (HalDisplayId::tryCast(display->getId())) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
@@ -3546,7 +3497,7 @@
}
void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
- for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) {
+ for (const auto& [token, displayDevice] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
auto display = displayDevice->getCompositionDisplay();
if (display->includesLayer(layer->getOutputFilter())) {
display->editState().dirtyRegion.orSelf(dirty);
@@ -4534,6 +4485,9 @@
if (what & layer_state_t::eAutoRefreshChanged) {
layer->setAutoRefresh(s.autoRefresh);
}
+ if (what & layer_state_t::eDimmingEnabledChanged) {
+ if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eTrustedOverlayChanged) {
if (layer->setTrustedOverlay(s.isTrustedOverlay)) {
flags |= eTraversalNeeded;
@@ -4832,7 +4786,8 @@
void SurfaceFlinger::initializeDisplays() {
// Async since we may be called from the main thread.
- static_cast<void>(mScheduler->schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
+ static_cast<void>(
+ mScheduler->schedule([this]() FTL_FAKE_GUARD(mStateLock) { onInitializeDisplays(); }));
}
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4932,7 +4887,7 @@
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
- auto future = mScheduler->schedule([=]() MAIN_THREAD {
+ auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4979,7 +4934,7 @@
bool dumpLayers = true;
{
- TimedLock lock(mStateLock, s2ns(1), __FUNCTION__);
+ TimedLock lock(mStateLock, s2ns(1), __func__);
if (!lock.locked()) {
StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
strerror(-lock.status), lock.status);
@@ -5067,8 +5022,6 @@
mFrameTimeline->parseArgs(args, result);
}
-// This should only be called from the main thread. Otherwise it would need
-// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
mDrawingState.traverse([&](Layer* layer) {
layer->logFrameStats();
@@ -5226,7 +5179,7 @@
}
void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const {
- for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+ for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
DisplayProto* displayProto = layersTraceProto.add_displays();
displayProto->set_id(display->getId().value);
displayProto->set_name(display->getDisplayName());
@@ -5867,7 +5820,7 @@
int64_t startingTime =
(fixedStartingTime) ? fixedStartingTime : systemTime();
mScheduler
- ->schedule([&]() MAIN_THREAD {
+ ->schedule([&]() FTL_FAKE_GUARD(mStateLock) {
mLayerTracing.notify("start", startingTime);
})
.wait();
@@ -5980,10 +5933,12 @@
switch (n = data.readInt32()) {
case 0:
case 1:
- ON_MAIN_THREAD(enableRefreshRateOverlay(static_cast<bool>(n)));
+ FTL_FAKE_GUARD(mStateLock,
+ enableRefreshRateOverlay(static_cast<bool>(n)));
break;
default: {
- reply->writeBool(ON_MAIN_THREAD(isRefreshRateOverlayEnabled()));
+ reply->writeBool(
+ FTL_FAKE_GUARD(mStateLock, isRefreshRateOverlayEnabled()));
}
}
});
@@ -6021,7 +5976,7 @@
return mScheduler
->schedule([this] {
const auto display =
- ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
// This is a little racy, but not in a way that hurts anything. As
// we grab the defaultMode from the display manager policy, we could
@@ -6043,7 +5998,7 @@
return mScheduler
->schedule([this] {
const auto display =
- ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
constexpr bool kOverridePolicy = true;
return setDesiredDisplayModeSpecsInternal(display, {},
kOverridePolicy);
@@ -6159,7 +6114,7 @@
// Update the overlay on the main thread to avoid race conditions with
// mRefreshRateConfigs->getActiveMode()
static_cast<void>(mScheduler->schedule([=] {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
ALOGW("%s: default display is null", __func__);
return;
@@ -6957,7 +6912,7 @@
}
auto future = mScheduler->schedule([=]() -> status_t {
- const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set desired display modes for invalid display token %p",
displayToken.get());
@@ -7202,7 +7157,7 @@
if (const auto frameRateOverride = mScheduler->getFrameRateOverride(uid)) {
refreshRate = *frameRateOverride;
} else if (!getHwComposer().isHeadless()) {
- if (const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked())) {
+ if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f2636b4..97b0e8d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -64,6 +64,7 @@
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
+#include "ThreadContext.h"
#include "TracedOrdinal.h"
#include "Tracing/LayerTracing.h"
#include "Tracing/TransactionTracing.h"
@@ -183,11 +184,6 @@
std::atomic<nsecs_t> mLastSwapTime = 0;
};
-struct SCOPED_CAPABILITY UnnecessaryLock {
- explicit UnnecessaryLock(Mutex& mutex) ACQUIRE(mutex) {}
- ~UnnecessaryLock() RELEASE() {}
-};
-
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
private IBinder::DeathRecipient,
@@ -740,7 +736,7 @@
void updateLayerGeometry();
void updateInputFlinger();
- void persistDisplayBrightness(bool needsComposite) REQUIRES(SF_MAIN_THREAD);
+ void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
std::vector<gui::DisplayInfo>& outDisplayInfos);
void commitInputWindowCommands() REQUIRES(mStateLock);
@@ -973,7 +969,7 @@
void setCompositorTimingSnapped(const DisplayStatInfo& stats,
nsecs_t compositeToPresentLatency);
- void postFrame();
+ void postFrame() REQUIRES(kMainThreadContext);
/*
* Display management
@@ -1088,7 +1084,7 @@
void clearStatsLocked(const DumpArgs& args, std::string& result);
void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
- void logFrameStats();
+ void logFrameStats() REQUIRES(kMainThreadContext);
void dumpVSync(std::string& result) const REQUIRES(mStateLock);
void dumpStaticScreenStats(std::string& result) const;
@@ -1431,7 +1427,7 @@
nsecs_t commitStart;
nsecs_t compositeStart;
nsecs_t presentEnd;
- } mPowerHintSessionData GUARDED_BY(SF_MAIN_THREAD);
+ } mPowerHintSessionData GUARDED_BY(kMainThreadContext);
nsecs_t mAnimationTransactionTimeout = s2ns(5);
diff --git a/services/surfaceflinger/ThreadContext.h b/services/surfaceflinger/ThreadContext.h
new file mode 100644
index 0000000..85c379d
--- /dev/null
+++ b/services/surfaceflinger/ThreadContext.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+// Enforces exclusive access by the main thread.
+constexpr class [[clang::capability("mutex")]] {
+} kMainThreadContext;
+
+} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 1d0f62b..a80aca2 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -22,6 +22,7 @@
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ftl/fake_guard.h>
#include <gui/LayerDebugInfo.h>
#include <gui/ScreenCaptureResults.h>
#include <gui/SurfaceComposerClient.h>
@@ -50,6 +51,7 @@
#include "SurfaceFlinger.h"
#include "SurfaceFlingerDefaultFactory.h"
#include "SurfaceInterceptor.h"
+#include "ThreadContext.h"
#include "TimeStats/TimeStats.h"
#include "renderengine/mock/RenderEngine.h"
@@ -445,7 +447,7 @@
mFlinger->clearStatsLocked(dumpArgs, result);
mFlinger->dumpTimeStats(dumpArgs, fdp->ConsumeBool(), result);
- mFlinger->logFrameStats();
+ FTL_FAKE_GUARD(kMainThreadContext, mFlinger->logFrameStats());
result = fdp->ConsumeRandomLengthString().c_str();
mFlinger->dumpFrameTimeline(dumpArgs, result);
@@ -651,7 +653,7 @@
updateCompositorTiming(&mFdp);
mFlinger->setCompositorTimingSnapped({}, mFdp.ConsumeIntegral<nsecs_t>());
- mFlinger->postFrame();
+ FTL_FAKE_GUARD(kMainThreadContext, mFlinger->postFrame());
mFlinger->calculateExpectedPresentTime({});
mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 168b576..704815d 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -28,6 +28,7 @@
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
"android.hardware.power@1.3",
+ "android.hardware.power-V2-cpp",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
new file mode 100644
index 0000000..e25a0ae
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "AidlPowerHalWrapperTest"
+
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <chrono>
+#include <memory>
+#include "DisplayHardware/PowerAdvisor.h"
+#include "android/hardware/power/WorkDuration.h"
+#include "binder/Status.h"
+#include "log/log_main.h"
+#include "mock/DisplayHardware/MockIPower.h"
+#include "mock/DisplayHardware/MockIPowerHintSession.h"
+#include "utils/Timers.h"
+
+using namespace android;
+using namespace android::Hwc2::mock;
+using namespace android::hardware::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+namespace android::Hwc2::impl {
+
+class AidlPowerHalWrapperTest : public testing::Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<AidlPowerHalWrapper> mWrapper = nullptr;
+ sp<NiceMock<MockIPower>> mMockHal = nullptr;
+ sp<NiceMock<MockIPowerHintSession>> mMockSession = nullptr;
+ void verifyAndClearExpectations();
+ void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
+ std::chrono::nanoseconds sleepBeforeLastSend);
+};
+
+void AidlPowerHalWrapperTest::SetUp() {
+ mMockHal = new NiceMock<MockIPower>();
+ mMockSession = new NiceMock<MockIPowerHintSession>();
+ ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
+ mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
+}
+
+void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
+ Mock::VerifyAndClearExpectations(mMockHal.get());
+ Mock::VerifyAndClearExpectations(mMockSession.get());
+}
+
+void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(
+ std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) {
+ for (size_t i = 0; i < durations.size(); i++) {
+ if (i == durations.size() - 1) {
+ std::this_thread::sleep_for(sleepBeforeLastSend);
+ }
+ auto duration = durations[i];
+ mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
+ }
+}
+WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) {
+ WorkDuration duration;
+ duration.durationNanos = durationNanos.count();
+ duration.timeStampNanos = timeStampNanos;
+ return duration;
+}
+
+namespace {
+TEST_F(AidlPowerHalWrapperTest, supportsPowerHintSession) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+ Mock::VerifyAndClearExpectations(mMockHal.get());
+ ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_))
+ .WillByDefault(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+ auto newWrapper = AidlPowerHalWrapper(mMockHal);
+ EXPECT_FALSE(newWrapper.supportsPowerHintSession());
+}
+
+TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ EXPECT_TRUE(mWrapper->startPowerHintSession());
+ EXPECT_FALSE(mWrapper->startPowerHintSession());
+}
+
+TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ threadIds = {2, 3};
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ EXPECT_CALL(*mMockSession.get(), close()).Times(1);
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
+ verifyAndClearExpectations();
+
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)).Times(0);
+ EXPECT_CALL(*mMockSession.get(), close()).Times(0);
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ verifyAndClearExpectations();
+}
+
+TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ std::chrono::nanoseconds base = 100ms;
+ // test cases with target work duration and whether it should update hint against baseline 100ms
+ const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false},
+ {-1ms, false},
+ {200ms, true},
+ {2ms, true},
+ {96ms, false},
+ {104ms, false}};
+
+ for (const auto& test : testCases) {
+ // reset to 100ms baseline
+ mWrapper->setTargetWorkDuration(1);
+ mWrapper->setTargetWorkDuration(base.count());
+
+ auto target = test.first;
+ EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count()))
+ .Times(test.second ? 1 : 0);
+ mWrapper->setTargetWorkDuration(target.count());
+ verifyAndClearExpectations();
+ }
+}
+
+TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+ mWrapper->setTargetWorkDuration(1);
+ EXPECT_TRUE(mWrapper->shouldReconnectHAL());
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ auto base = toWorkDuration(100ms, 0);
+ // test cases with actual work durations and whether it should update hint against baseline
+ // 100ms
+ const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
+ testCases = {{{{-1ms, 100}}, false},
+ {{{91ms, 100}}, false},
+ {{{109ms, 100}}, false},
+ {{{100ms, 100}, {200ms, 200}}, true},
+ {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
+
+ for (const auto& test : testCases) {
+ // reset actual duration
+ sendActualWorkDurationGroup({base}, 80ms);
+
+ auto raw = test.first;
+ std::vector<WorkDuration> durations(raw.size());
+ std::transform(raw.begin(), raw.end(), durations.begin(),
+ [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
+ return toWorkDuration(d.first, d.second);
+ });
+ EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
+ .Times(test.second ? 1 : 0);
+ sendActualWorkDurationGroup(durations, 0ms);
+ verifyAndClearExpectations();
+ }
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ auto base = toWorkDuration(100ms, 0);
+ // test cases with actual work durations and whether it should update hint against baseline
+ // 100ms
+ const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
+ testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}};
+
+ for (const auto& test : testCases) {
+ // reset actual duration
+ sendActualWorkDurationGroup({base}, 80ms);
+
+ auto raw = test.first;
+ std::vector<WorkDuration> durations(raw.size());
+ std::transform(raw.begin(), raw.end(), durations.begin(),
+ [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
+ return toWorkDuration(d.first, d.second);
+ });
+ EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
+ .Times(test.second ? 1 : 0);
+ sendActualWorkDurationGroup(durations, 80ms);
+ verifyAndClearExpectations();
+ }
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+ WorkDuration duration;
+ duration.durationNanos = 1;
+ EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+ sendActualWorkDurationGroup({duration}, 0ms);
+ EXPECT_TRUE(mWrapper->shouldReconnectHAL());
+}
+
+} // namespace
+} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1eea023..cc9d48c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -26,6 +26,8 @@
srcs: [
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockHWC2.cpp",
+ "mock/DisplayHardware/MockIPower.cpp",
+ "mock/DisplayHardware/MockIPowerHintSession.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
@@ -67,6 +69,7 @@
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
+ "AidlPowerHalWrapperTest.cpp",
"CachingTest.cpp",
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
index 73c60e1..225ad16 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
@@ -19,6 +19,7 @@
#include "DisplayTransactionTestHelpers.h"
+#include <ftl/fake_guard.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -35,7 +36,7 @@
};
TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessNoComposite) {
- MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+ ftl::FakeGuard guard(kMainThreadContext);
sp<DisplayDevice> displayDevice = getDisplayDevice();
EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
@@ -52,7 +53,7 @@
}
TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithComposite) {
- MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+ ftl::FakeGuard guard(kMainThreadContext);
sp<DisplayDevice> displayDevice = getDisplayDevice();
EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
@@ -70,7 +71,7 @@
}
TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithCompositeShortCircuitsOnNoOp) {
- MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+ ftl::FakeGuard guard(kMainThreadContext);
sp<DisplayDevice> displayDevice = getDisplayDevice();
EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
new file mode 100644
index 0000000..2323ebb
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/DisplayHardware/MockIPower.h"
+
+namespace android::Hwc2::mock {
+
+// Explicit default instantiation is recommended.
+MockIPower::MockIPower() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
new file mode 100644
index 0000000..0ddc90d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "binder/Status.h"
+
+#include <android/hardware/power/IPower.h>
+#include <gmock/gmock.h>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
+using android::hardware::power::Mode;
+
+namespace android::Hwc2::mock {
+
+class MockIPower : public IPower {
+public:
+ MockIPower();
+
+ MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(Status, createHintSession,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, sp<IPowerHintSession>* session),
+ (override));
+ MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
new file mode 100644
index 0000000..770bc15
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/DisplayHardware/MockIPowerHintSession.h"
+
+namespace android::Hwc2::mock {
+
+// Explicit default instantiation is recommended.
+MockIPowerHintSession::MockIPowerHintSession() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
new file mode 100644
index 0000000..439f6f4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "binder/Status.h"
+
+#include <android/hardware/power/IPower.h>
+#include <gmock/gmock.h>
+
+using android::binder::Status;
+using android::hardware::power::IPowerHintSession;
+
+using namespace android::hardware::power;
+
+namespace android::Hwc2::mock {
+
+class MockIPowerHintSession : public IPowerHintSession {
+public:
+ MockIPowerHintSession();
+
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(Status, pause, (), (override));
+ MOCK_METHOD(Status, resume, (), (override));
+ MOCK_METHOD(Status, close, (), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
+ MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
+};
+
+} // namespace android::Hwc2::mock