Merge "Rename cache flag due to gantry broken." into main
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index e54f9d3..870e8eb 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -44,6 +44,7 @@
#include "Timeout.h"
#include "utils.h"
+using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hidl::base::V1_0::DebugInfo;
@@ -522,27 +523,35 @@
using namespace ::android::hardware;
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
- using std::literals::chrono_literals::operator""s;
- auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
- std::map<std::string, TableEntry> entries;
- for (const auto &info : infos) {
- std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
- std::string{info.instanceName.c_str()};
- entries.emplace(interfaceName, TableEntry{
- .interfaceName = interfaceName,
- .transport = vintf::Transport::PASSTHROUGH,
- .clientPids = info.clientPids,
- }).first->second.arch |= fromBaseArchitecture(info.arch);
- }
- for (auto &&pair : entries) {
- putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second));
- }
- });
+
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if ret.isOk(), the background thread has
+ // already ended, so it is safe to dereference entries.
+ auto entries = std::make_shared<std::map<std::string, TableEntry>>();
+ auto ret =
+ timeoutIPC(mLshal.getDebugDumpWait(), manager, &IServiceManager::debugDump,
+ [entries](const auto& infos) {
+ for (const auto& info : infos) {
+ std::string interfaceName = std::string{info.interfaceName.c_str()} +
+ "/" + std::string{info.instanceName.c_str()};
+ entries->emplace(interfaceName,
+ TableEntry{
+ .interfaceName = interfaceName,
+ .transport = vintf::Transport::PASSTHROUGH,
+ .clientPids = info.clientPids,
+ })
+ .first->second.arch |= fromBaseArchitecture(info.arch);
+ }
+ });
if (!ret.isOk()) {
err() << "Error: Failed to call list on getPassthroughServiceManager(): "
<< ret.description() << std::endl;
return DUMP_ALL_LIBS_ERROR;
}
+ for (auto&& pair : *entries) {
+ putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second));
+ }
return OK;
}
@@ -553,27 +562,40 @@
using namespace ::android::hardware::details;
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
- auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
- for (const auto &info : infos) {
- if (info.clientPids.size() <= 0) {
- continue;
- }
- putEntry(HalType::PASSTHROUGH_CLIENTS, {
- .interfaceName =
- std::string{info.interfaceName.c_str()} + "/" +
- std::string{info.instanceName.c_str()},
- .transport = vintf::Transport::PASSTHROUGH,
- .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
- .clientPids = info.clientPids,
- .arch = fromBaseArchitecture(info.arch)
- });
- }
- });
+
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if ret.isOk(), the background thread has
+ // already ended, so it is safe to dereference entries.
+ auto entries = std::make_shared<std::vector<TableEntry>>();
+ auto ret =
+ timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::debugDump,
+ [entries](const auto& infos) {
+ for (const auto& info : infos) {
+ if (info.clientPids.size() <= 0) {
+ continue;
+ }
+ entries->emplace_back(
+ TableEntry{.interfaceName =
+ std::string{info.interfaceName.c_str()} +
+ "/" +
+ std::string{info.instanceName.c_str()},
+ .transport = vintf::Transport::PASSTHROUGH,
+ .serverPid = info.clientPids.size() == 1
+ ? info.clientPids[0]
+ : NO_PID,
+ .clientPids = info.clientPids,
+ .arch = fromBaseArchitecture(info.arch)});
+ }
+ });
if (!ret.isOk()) {
err() << "Error: Failed to call debugDump on defaultServiceManager(): "
<< ret.description() << std::endl;
return DUMP_PASSTHROUGH_ERROR;
}
+ for (auto&& entry : *entries) {
+ putEntry(HalType::PASSTHROUGH_CLIENTS, std::move(entry));
+ }
return OK;
}
@@ -583,11 +605,14 @@
if (!shouldFetchHalType(HalType::BINDERIZED_SERVICES)) { return OK; }
const vintf::Transport mode = vintf::Transport::HWBINDER;
- hidl_vec<hidl_string> fqInstanceNames;
- // copying out for timeoutIPC
- auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
- fqInstanceNames = names;
- });
+
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if listRet.isOk(), the background thread has
+ // already ended, so it is safe to dereference fqInstanceNames.
+ auto fqInstanceNames = std::make_shared<hidl_vec<hidl_string>>();
+ auto listRet = timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::list,
+ [fqInstanceNames](const auto& names) { *fqInstanceNames = names; });
if (!listRet.isOk()) {
err() << "Error: Failed to list services for " << mode << ": "
<< listRet.description() << std::endl;
@@ -596,7 +621,7 @@
Status status = OK;
std::map<std::string, TableEntry> allTableEntries;
- for (const auto &fqInstanceName : fqInstanceNames) {
+ for (const auto& fqInstanceName : *fqInstanceNames) {
// create entry and default assign all fields.
TableEntry& entry = allTableEntries[fqInstanceName];
entry.interfaceName = fqInstanceName;
@@ -623,7 +648,8 @@
const auto pair = splitFirst(entry->interfaceName, '/');
const auto &serviceName = pair.first;
const auto &instanceName = pair.second;
- auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+ auto getRet = timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::get, serviceName,
+ instanceName);
if (!getRet.isOk()) {
handleError(TRANSACTION_ERROR,
"cannot be fetched from service manager:" + getRet.description());
@@ -637,30 +663,33 @@
// getDebugInfo
do {
- DebugInfo debugInfo;
- auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) {
- debugInfo = received;
- });
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if debugRet.isOk(), the background thread has
+ // already ended, so it is safe to dereference debugInfo.
+ auto debugInfo = std::make_shared<DebugInfo>();
+ auto debugRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::getDebugInfo,
+ [debugInfo](const auto& received) { *debugInfo = received; });
if (!debugRet.isOk()) {
handleError(TRANSACTION_ERROR,
"debugging information cannot be retrieved: " + debugRet.description());
break; // skip getPidInfo
}
- entry->serverPid = debugInfo.pid;
- entry->serverObjectAddress = debugInfo.ptr;
- entry->arch = fromBaseArchitecture(debugInfo.arch);
+ entry->serverPid = debugInfo->pid;
+ entry->serverObjectAddress = debugInfo->ptr;
+ entry->arch = fromBaseArchitecture(debugInfo->arch);
- if (debugInfo.pid != NO_PID) {
- const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
+ if (debugInfo->pid != NO_PID) {
+ const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo->pid);
if (pidInfo == nullptr) {
handleError(IO_ERROR,
- "no information for PID " + std::to_string(debugInfo.pid) +
- ", are you root?");
+ "no information for PID " + std::to_string(debugInfo->pid) +
+ ", are you root?");
break;
}
- if (debugInfo.ptr != NO_PTR) {
- auto it = pidInfo->refPids.find(debugInfo.ptr);
+ if (debugInfo->ptr != NO_PTR) {
+ auto it = pidInfo->refPids.find(debugInfo->ptr);
if (it != pidInfo->refPids.end()) {
entry->clientPids = it->second;
}
@@ -672,39 +701,46 @@
// hash
do {
- ssize_t hashIndex = -1;
- auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) {
- for (size_t i = 0; i < c.size(); ++i) {
- if (serviceName == c[i]) {
- hashIndex = static_cast<ssize_t>(i);
- break;
- }
- }
- });
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ auto hashIndexStore = std::make_shared<ssize_t>(-1);
+ auto ifaceChainRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::interfaceChain,
+ [hashIndexStore, serviceName](const auto& c) {
+ for (size_t i = 0; i < c.size(); ++i) {
+ if (serviceName == c[i]) {
+ *hashIndexStore = static_cast<ssize_t>(i);
+ break;
+ }
+ }
+ });
if (!ifaceChainRet.isOk()) {
handleError(TRANSACTION_ERROR,
"interfaceChain fails: " + ifaceChainRet.description());
break; // skip getHashChain
}
+ // if ifaceChainRet.isOk(), the background thread has already ended, so it is safe to
+ // dereference hashIndex without any locking.
+ auto hashIndex = *hashIndexStore;
if (hashIndex < 0) {
handleError(BAD_IMPL, "Interface name does not exist in interfaceChain.");
break; // skip getHashChain
}
- auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) {
- if (static_cast<size_t>(hashIndex) >= hashChain.size()) {
- handleError(BAD_IMPL,
- "interfaceChain indicates position " + std::to_string(hashIndex) +
- " but getHashChain returns " + std::to_string(hashChain.size()) +
- " hashes");
- return;
- }
-
- auto&& hashArray = hashChain[hashIndex];
- entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
- });
+ // See comments about hashIndex above.
+ auto hashChain = std::make_shared<hidl_vec<hidl_array<uint8_t, 32>>>();
+ auto hashRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::getHashChain,
+ [hashChain](const auto& ret) { *hashChain = std::move(ret); });
if (!hashRet.isOk()) {
handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
}
+ if (static_cast<size_t>(hashIndex) >= hashChain->size()) {
+ handleError(BAD_IMPL,
+ "interfaceChain indicates position " + std::to_string(hashIndex) +
+ " but getHashChain returns " + std::to_string(hashChain->size()) +
+ " hashes");
+ } else {
+ auto&& hashArray = (*hashChain)[hashIndex];
+ entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
+ }
} while (0);
if (status == OK) {
entry->serviceStatus = ServiceStatus::ALIVE;
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index a5f98c2..6115da7 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -250,5 +250,17 @@
return mPassthroughManager;
}
+void Lshal::setWaitTimeForTest(std::chrono::milliseconds ipcCallWait,
+ std::chrono::milliseconds debugDumpWait) {
+ mIpcCallWait = ipcCallWait;
+ mDebugDumpWait = debugDumpWait;
+}
+std::chrono::milliseconds Lshal::getIpcCallWait() const {
+ return mIpcCallWait;
+}
+std::chrono::milliseconds Lshal::getDebugDumpWait() const {
+ return mDebugDumpWait;
+}
+
} // namespace lshal
} // namespace android
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index 50279d4..cb2820c 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <iostream>
#include <string>
@@ -58,6 +59,11 @@
void forEachCommand(const std::function<void(const Command* c)>& f) const;
+ void setWaitTimeForTest(std::chrono::milliseconds ipcCallWait,
+ std::chrono::milliseconds debugDumpWait);
+ std::chrono::milliseconds getIpcCallWait() const;
+ std::chrono::milliseconds getDebugDumpWait() const;
+
private:
Status parseArgs(const Arg &arg);
@@ -70,6 +76,9 @@
std::vector<std::unique_ptr<Command>> mRegisteredCommands;
+ std::chrono::milliseconds mIpcCallWait{500};
+ std::chrono::milliseconds mDebugDumpWait{10000};
+
DISALLOW_COPY_AND_ASSIGN(Lshal);
};
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
index e8d22d9..805e8dc 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -27,8 +27,6 @@
namespace android {
namespace lshal {
-static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
-
class BackgroundTaskState {
public:
explicit BackgroundTaskState(std::function<void(void)> &&func)
@@ -96,12 +94,5 @@
return ret;
}
-template<class Function, class I, class... Args>
-typename std::result_of<Function(I *, Args...)>::type
-timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
- return timeoutIPC(IPC_CALL_WAIT, interfaceObject, func, args...);
-}
-
-
} // namespace lshal
} // namespace android
diff --git a/include/ftl/details/function.h b/include/ftl/details/function.h
new file mode 100644
index 0000000..35c5a8b
--- /dev/null
+++ b/include/ftl/details/function.h
@@ -0,0 +1,135 @@
+/*
+ * 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 <array>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <type_traits>
+
+namespace android::ftl::details {
+
+// The maximum allowed value for the template argument `N` in
+// `ftl::Function<F, N>`.
+constexpr size_t kFunctionMaximumN = 14;
+
+// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member
+// function type `Ret(Args...)`.
+
+template <typename>
+struct remove_member_function_pointer;
+
+template <typename Class, typename Ret, typename... Args>
+struct remove_member_function_pointer<Ret (Class::*)(Args...)> {
+ using type = Ret(Args...);
+};
+
+template <typename Class, typename Ret, typename... Args>
+struct remove_member_function_pointer<Ret (Class::*)(Args...) const> {
+ using type = Ret(Args...);
+};
+
+template <auto MemberFunction>
+using remove_member_function_pointer_t =
+ typename remove_member_function_pointer<decltype(MemberFunction)>::type;
+
+// Helper functions for binding to the supported targets.
+
+template <typename Ret, typename... Args>
+auto bind_opaque_no_op() -> Ret (*)(void*, Args...) {
+ return [](void*, Args...) -> Ret {
+ if constexpr (!std::is_void_v<Ret>) {
+ return Ret{};
+ }
+ };
+}
+
+template <typename F, typename Ret, typename... Args>
+auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) {
+ return [](void* opaque, Args... args) -> Ret {
+ return std::invoke(*static_cast<F*>(opaque), std::forward<Args>(args)...);
+ };
+}
+
+template <auto MemberFunction, typename Class, typename Ret, typename... Args>
+auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) {
+ return [instance](Args... args) -> Ret {
+ return std::invoke(MemberFunction, instance, std::forward<Args>(args)...);
+ };
+}
+
+template <auto FreeFunction, typename Ret, typename... Args>
+auto bind_free_function(Ret (*)(Args...) = nullptr) {
+ return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward<Args>(args)...); };
+}
+
+// Traits class for the opaque storage used by Function.
+
+template <std::size_t N>
+struct function_opaque_storage {
+ // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size,
+ // which allows a lambda with zero or one capture args.
+ using type = std::array<std::intptr_t, N + 1>;
+
+ template <typename S>
+ static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v<S>;
+
+ template <typename S>
+ static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v<S>;
+
+ template <typename S>
+ static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type);
+
+ template <typename S>
+ static constexpr bool require_alignment_compatible =
+ std::alignment_of_v<S> <= std::alignment_of_v<type>;
+
+ // Copies `src` into the opaque storage, and returns that storage.
+ template <typename S>
+ static type opaque_copy(const S& src) {
+ // TODO: Replace with C++20 concepts/constraints which can give more details.
+ static_assert(require_trivially_copyable<S>,
+ "ftl::Function can only store lambdas that capture trivially copyable data.");
+ static_assert(
+ require_trivially_destructible<S>,
+ "ftl::Function can only store lambdas that capture trivially destructible data.");
+ static_assert(require_will_fit_in_opaque_storage<S>,
+ "ftl::Function has limited storage for lambda captured state. Maybe you need to "
+ "increase N?");
+ static_assert(require_alignment_compatible<S>);
+
+ type opaque;
+ std::memcpy(opaque.data(), &src, sizeof(S));
+ return opaque;
+ }
+};
+
+// Traits class to help determine the template parameters to use for a ftl::Function, given a
+// function object.
+
+template <typename F, typename = decltype(&F::operator())>
+struct function_traits {
+ // The function type `F` with which to instantiate the `Function<F, N>` template.
+ using type = remove_member_function_pointer_t<&F::operator()>;
+
+ // The (minimum) size `N` with which to instantiate the `Function<F, N>` template.
+ static constexpr std::size_t size =
+ (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t);
+};
+
+} // namespace android::ftl::details
diff --git a/include/ftl/function.h b/include/ftl/function.h
new file mode 100644
index 0000000..3538ca4
--- /dev/null
+++ b/include/ftl/function.h
@@ -0,0 +1,297 @@
+/*
+ * 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 <cstddef>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/details/function.h>
+
+namespace android::ftl {
+
+// ftl::Function<F, N> is a container for function object, and can mostly be used in place of
+// std::function<F>.
+//
+// Unlike std::function<F>, a ftl::Function<F, N>:
+//
+// * Uses a static amount of memory (controlled by N), and never any dynamic allocation.
+// * Satisfies the std::is_trivially_copyable<> trait.
+// * Satisfies the std::is_trivially_destructible<> trait.
+//
+// However those same limits are also required from the contained function object in turn.
+//
+// The size of a ftl::Function<F, N> is guaranteed to be:
+//
+// sizeof(std::intptr_t) * (N + 2)
+//
+// A ftl::Function<F, N> can always be implicitly converted to a larger size ftl::Function<F, M>.
+// Trying to convert the other way leads to a compilation error.
+//
+// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns
+// false in this state. It is undefined behavior to attempt to invoke the function in this state.
+//
+// The ftl::Function<F, N> can also be constructed or assigned from ftl::no_op. This sets up the
+// ftl::Function to be non-empty, with a function that when called does nothing except
+// default-constructs a return value.
+//
+// The ftl::make_function() helpers construct a ftl::Function<F, N>, including deducing the
+// values of F and N from the arguments it is given.
+//
+// The static ftl::Function<F, N>::make() helpers construct a ftl::Function<F, N> without that
+// deduction, and also allow for implicit argument conversion if the target being called needs them.
+//
+// The construction helpers allow any of the following types of functions to be stored:
+//
+// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small
+// capture, or other "functor". The requirements are:
+//
+// 1) The function object must be trivial to destroy (in fact, the destructor will never
+// actually be called once copied to the internal storage).
+// 2) The function object must be trivial to copy (the raw bytes will be copied as the
+// ftl::Function<F, N> is copied/moved).
+// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1),
+// and it cannot require stricter alignment than alignof(std::intptr_t).
+//
+// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is
+// enough to capture `this`, which is why N=0 is the default.
+//
+// * A member function, with the address passed as the template value argument to the construction
+// helper function, along with the instance pointer needed to invoke it passed as an ordinary
+// argument.
+//
+// ftl::make_function<&Class::member_function>(this);
+//
+// Note that the indicated member function will be invoked non-virtually. If you need it to be
+// invoked virtually, you should invoke it yourself with a small lambda like so:
+//
+// ftl::function([this] { virtual_member_function(); });
+//
+// * An ordinary function ("free function"), with the address of the function passed as a template
+// value argument.
+//
+// ftl::make_function<&std::atoi>();
+//
+// As with the member function helper, as the function is known at compile time, it will be called
+// directly.
+//
+// Example usage:
+//
+// class MyClass {
+// public:
+// void on_event() const {}
+// int on_string(int*, std::string_view) { return 1; }
+//
+// auto get_function() {
+// return ftl::function([this] { on_event(); });
+// }
+// } cls;
+//
+// // A function container with no arguments, and returning no value.
+// ftl::Function<void()> f;
+//
+// // Construct a ftl::Function containing a small lambda.
+// f = cls.get_function();
+//
+// // Construct a ftl::Function that calls `cls.on_event()`.
+// f = ftl::function<&MyClass::on_event>(&cls);
+//
+// // Create a do-nothing function.
+// f = ftl::no_op;
+//
+// // Invoke the contained function.
+// f();
+//
+// // Also invokes it.
+// std::invoke(f);
+//
+// // Create a typedef to give a more meaningful name and bound the size.
+// using MyFunction = ftl::Function<int(std::string_view), 2>;
+// int* ptr = nullptr;
+// auto f1 = MyFunction::make_function(
+// [cls = &cls, ptr](std::string_view sv) {
+// return cls->on_string(ptr, sv);
+// });
+// int r = f1("abc"sv);
+//
+// // Returns a default-constructed int (0).
+// f1 = ftl::no_op;
+// r = f1("abc"sv);
+// assert(r == 0);
+
+template <typename F, std::size_t N = 0>
+class Function;
+
+// Used to construct a Function that does nothing.
+struct NoOpTag {};
+
+constexpr NoOpTag no_op;
+
+// Detects that a type is a `ftl::Function<F, N>` regardless of what `F` and `N` are.
+template <typename>
+struct is_function : public std::false_type {};
+
+template <typename F, std::size_t N>
+struct is_function<Function<F, N>> : public std::true_type {};
+
+template <typename T>
+constexpr bool is_function_v = is_function<T>::value;
+
+template <typename Ret, typename... Args, std::size_t N>
+class Function<Ret(Args...), N> final {
+ // Enforce a valid size, with an arbitrary maximum allowed size for the container of
+ // sizeof(std::intptr_t) * 16, though that maximum can be relaxed.
+ static_assert(N <= details::kFunctionMaximumN);
+
+ using OpaqueStorageTraits = details::function_opaque_storage<N>;
+
+ public:
+ // Defining result_type allows ftl::Function to be substituted for std::function.
+ using result_type = Ret;
+
+ // Constructs an empty ftl::Function.
+ Function() = default;
+
+ // Constructing or assigning from nullptr_t also creates an empty ftl::Function.
+ Function(std::nullptr_t) {}
+ Function& operator=(std::nullptr_t) { return *this = Function(nullptr); }
+
+ // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which
+ // returns a default constructed return value.
+ Function(NoOpTag) : function_(details::bind_opaque_no_op<Ret, Args...>()) {}
+ Function& operator=(NoOpTag) { return *this = Function(no_op); }
+
+ // Constructing/assigning from a function object stores a copy of that function object, however:
+ // * It must be trivially copyable, as the implementation makes a copy with memcpy().
+ // * It must be trivially destructible, as the implementation doesn't destroy the copy!
+ // * It must fit in the limited internal storage, which enforces size/alignment restrictions.
+
+ template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>>
+ Function(const F& f)
+ : opaque_(OpaqueStorageTraits::opaque_copy(f)),
+ function_(details::bind_opaque_function_object<F, Ret, Args...>(f)) {}
+
+ template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>>
+ Function& operator=(const F& f) noexcept {
+ return *this = Function{OpaqueStorageTraits::opaque_copy(f),
+ details::bind_opaque_function_object<F, Ret, Args...>(f)};
+ }
+
+ // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else.
+
+ template <std::size_t M>
+ Function(const Function<Ret(Args...), M>& other)
+ : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, function_(other.function_) {}
+
+ template <std::size_t M>
+ auto& operator=(const Function<Ret(Args...), M>& other) {
+ return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), other.function_};
+ }
+
+ // Returns true if a function is set.
+ explicit operator bool() const { return function_ != nullptr; }
+
+ // Checks if the other function has the same contents as this one.
+ bool operator==(const Function& other) const {
+ return other.opaque_ == opaque_ && other.function_ == function_;
+ }
+ bool operator!=(const Function& other) const { return !operator==(other); }
+
+ // Alternative way of testing for a function being set.
+ bool operator==(std::nullptr_t) const { return function_ == nullptr; }
+ bool operator!=(std::nullptr_t) const { return function_ != nullptr; }
+
+ // Invokes the function.
+ Ret operator()(Args... args) const {
+ return std::invoke(function_, opaque_.data(), std::forward<Args>(args)...);
+ }
+
+ // Creation helper for function objects, such as lambdas.
+ template <typename F>
+ static auto make(const F& f) -> decltype(Function{f}) {
+ return Function{f};
+ }
+
+ // Creation helper for a class pointer and a compile-time chosen member function to call.
+ template <auto MemberFunction, typename Class>
+ static auto make(Class* instance) -> decltype(Function{
+ details::bind_member_function<MemberFunction>(instance,
+ static_cast<Ret (*)(Args...)>(nullptr))}) {
+ return Function{details::bind_member_function<MemberFunction>(
+ instance, static_cast<Ret (*)(Args...)>(nullptr))};
+ }
+
+ // Creation helper for a compile-time chosen free function to call.
+ template <auto FreeFunction>
+ static auto make() -> decltype(Function{
+ details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}) {
+ return Function{
+ details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))};
+ }
+
+ private:
+ // Needed so a Function<F, M> can be converted to a Function<F, N>.
+ template <typename, std::size_t>
+ friend class Function;
+
+ // The function pointer type of function stored in `function_`. The first argument is always
+ // `&opaque_`.
+ using StoredFunction = Ret(void*, Args...);
+
+ // The type of the opaque storage, used to hold an appropriate function object.
+ // The type stored here is ONLY known to the StoredFunction.
+ // We always use at least one std::intptr_t worth of storage, and always a multiple of that size.
+ using OpaqueStorage = typename OpaqueStorageTraits::type;
+
+ // Internal constructor for creating from a raw opaque blob + function pointer.
+ Function(const OpaqueStorage& opaque, StoredFunction* function)
+ : opaque_(opaque), function_(function) {}
+
+ // Note: `mutable` so that `operator() const` can use it.
+ mutable OpaqueStorage opaque_{};
+ StoredFunction* function_{nullptr};
+};
+
+// Makes a ftl::Function given a function object `F`.
+template <typename F, typename T = details::function_traits<F>>
+Function(const F&) -> Function<typename T::type, T::size>;
+
+template <typename F>
+auto make_function(const F& f) -> decltype(Function{f}) {
+ return Function{f};
+}
+
+// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`.
+template <auto MemberFunction, typename Class>
+auto make_function(Class* instance)
+ -> decltype(Function{details::bind_member_function<MemberFunction>(
+ instance,
+ static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}) {
+ return Function{details::bind_member_function<MemberFunction>(
+ instance, static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))};
+}
+
+// Makes a ftl::Function given an ordinary free function.
+template <auto FreeFunction>
+auto make_function() -> decltype(Function{
+ details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}) {
+ return Function{
+ details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))};
+}
+
+} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 7457496..b0eceef 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -130,9 +130,9 @@
"isActive=[%d]",
ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
- orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
- physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
- deviceHeight, isActive);
+ static_cast<int>(orientation), logicalLeft, logicalTop, logicalRight,
+ logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom,
+ deviceWidth, deviceHeight, isActive);
}
};
diff --git a/include/input/Input.h b/include/input/Input.h
index 1c4ea6b..7b253a5 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -515,6 +515,8 @@
PointerProperties& operator=(const PointerProperties&) = default;
};
+std::ostream& operator<<(std::ostream& out, const PointerProperties& properties);
+
// TODO(b/211379801) : Use a strong type from ftl/mixins.h instead
using DeviceId = int32_t;
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 63c0e40..3470be4 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -20,6 +20,7 @@
#include <map>
#include <optional>
#include <set>
+#include <sstream>
#include <string>
#include <vector>
@@ -33,6 +34,13 @@
return bitset.to_string();
}
+template <class T>
+std::string streamableToString(const T& streamable) {
+ std::stringstream out;
+ out << streamable;
+ return out.str();
+}
+
template <typename T>
inline std::string constToString(const T& v) {
return std::to_string(v);
@@ -109,11 +117,12 @@
template <typename T>
std::string dumpVector(const std::vector<T>& values,
std::string (*valueToString)(const T&) = constToString) {
- std::string dump = valueToString(values[0]);
- for (size_t i = 1; i < values.size(); i++) {
- dump += ", " + valueToString(values[i]);
+ std::string out;
+ for (const auto& value : values) {
+ out += out.empty() ? "[" : ", ";
+ out += valueToString(value);
}
- return dump;
+ return out.empty() ? "[]" : (out + "]");
}
const char* toString(bool value);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index f1cc5f0..ae0fb01 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -22,23 +22,51 @@
}
cc_library_headers {
- name: "libbinder_headers",
+ name: "libbinder_headers_base",
export_include_dirs: ["include"],
vendor_available: true,
recovery_available: true,
host_supported: true,
- // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+
+ header_libs: [
+ "libbinder_headers_platform_shared",
+ ],
+ export_header_lib_headers: [
+ "libbinder_headers_platform_shared",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_headers {
+ name: "libbinder_headers",
+ vendor_available: true,
+ recovery_available: true,
+ host_supported: true,
native_bridge_supported: true,
header_libs: [
"libbase_headers",
- "libbinder_headers_platform_shared",
+ "libbinder_headers_base",
"libcutils_headers",
"libutils_headers",
],
export_header_lib_headers: [
"libbase_headers",
- "libbinder_headers_platform_shared",
+ "libbinder_headers_base",
"libcutils_headers",
"libutils_headers",
],
@@ -87,6 +115,7 @@
"RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
+ "RpcTransportRaw.cpp",
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
@@ -94,17 +123,8 @@
"file.cpp",
],
- shared_libs: [
- "libcutils",
- "libutils",
- ],
-
- static_libs: [
- "libbase",
- ],
-
header_libs: [
- "libbinder_headers",
+ "libbinder_headers_base",
],
cflags: [
@@ -131,7 +151,6 @@
srcs: [
"OS_android.cpp",
"OS_unix_base.cpp",
- "RpcTransportRaw.cpp",
],
target: {
@@ -156,11 +175,18 @@
},
shared_libs: [
+ "libcutils",
"liblog",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
],
header_libs: [
"jni_headers",
+ "libbinder_headers",
],
export_header_lib_headers: [
@@ -217,17 +243,24 @@
host_supported: true,
header_libs: [
+ "libbinder_headers_base",
"liblog_stub",
"trusty_mock_headers",
],
+ shared_libs: [
+ "libutils_binder_sdk",
+ ],
+
cflags: [
"-DBINDER_RPC_SINGLE_THREADED",
"-DBINDER_ENABLE_LIBLOG_ASSERT",
"-DBINDER_DISABLE_NATIVE_HANDLE",
"-DBINDER_DISABLE_BLOB",
"-DBINDER_NO_LIBBASE",
+ // TODO: switch to "vendor: true" rather than copying this
// Trusty libbinder uses vendor stability for its binders
+ "-D__ANDROID_VENDOR__",
"-D__ANDROID_VNDK__",
"-U__ANDROID__",
"-D__TRUSTY__",
@@ -351,6 +384,44 @@
afdo: true,
}
+cc_library_host_shared {
+ name: "libbinder_sdk",
+
+ defaults: [
+ "libbinder_common_defaults",
+ ],
+
+ shared_libs: [
+ "libutils_binder_sdk",
+ ],
+
+ cflags: [
+ "-DBINDER_ENABLE_LIBLOG_ASSERT",
+ "-DBINDER_DISABLE_NATIVE_HANDLE",
+ "-DBINDER_DISABLE_BLOB",
+ "-DBINDER_NO_LIBBASE",
+ ],
+
+ header_libs: [
+ "liblog_stub",
+ ],
+
+ srcs: [
+ "OS_non_android_linux.cpp",
+ "OS_unix_base.cpp",
+ ],
+
+ visibility: [
+ ":__subpackages__",
+ ],
+
+ target: {
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
cc_library_static {
name: "libbinder_rpc_no_kernel",
vendor_available: true,
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 9341eff..b92e504 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -67,28 +67,28 @@
// Static const and functions will be optimized out if not used,
// when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out.
-static const char *kReturnStrings[] = {
- "BR_ERROR",
- "BR_OK",
- "BR_TRANSACTION",
- "BR_REPLY",
- "BR_ACQUIRE_RESULT",
- "BR_DEAD_REPLY",
- "BR_TRANSACTION_COMPLETE",
- "BR_INCREFS",
- "BR_ACQUIRE",
- "BR_RELEASE",
- "BR_DECREFS",
- "BR_ATTEMPT_ACQUIRE",
- "BR_NOOP",
- "BR_SPAWN_LOOPER",
- "BR_FINISHED",
- "BR_DEAD_BINDER",
- "BR_CLEAR_DEATH_NOTIFICATION_DONE",
- "BR_FAILED_REPLY",
- "BR_FROZEN_REPLY",
- "BR_ONEWAY_SPAM_SUSPECT",
- "BR_TRANSACTION_SEC_CTX",
+static const char* kReturnStrings[] = {
+ "BR_ERROR",
+ "BR_OK",
+ "BR_TRANSACTION/BR_TRANSACTION_SEC_CTX",
+ "BR_REPLY",
+ "BR_ACQUIRE_RESULT",
+ "BR_DEAD_REPLY",
+ "BR_TRANSACTION_COMPLETE",
+ "BR_INCREFS",
+ "BR_ACQUIRE",
+ "BR_RELEASE",
+ "BR_DECREFS",
+ "BR_ATTEMPT_ACQUIRE",
+ "BR_NOOP",
+ "BR_SPAWN_LOOPER",
+ "BR_FINISHED",
+ "BR_DEAD_BINDER",
+ "BR_CLEAR_DEATH_NOTIFICATION_DONE",
+ "BR_FAILED_REPLY",
+ "BR_FROZEN_REPLY",
+ "BR_ONEWAY_SPAM_SUSPECT",
+ "BR_TRANSACTION_PENDING_FROZEN",
};
static const char *kCommandStrings[] = {
diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp
new file mode 100644
index 0000000..b525d1a
--- /dev/null
+++ b/libs/binder/OS_non_android_linux.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OS.h"
+
+#include <log/log.h>
+
+#include <syscall.h>
+#include <cstdarg>
+
+#ifdef __ANDROID__
+#error "This module is not intended for Android, just bare Linux"
+#endif
+#ifdef __APPLE__
+#error "This module is not intended for MacOS"
+#endif
+#ifdef _WIN32
+#error "This module is not intended for Windows"
+#endif
+
+namespace android::binder::os {
+
+void trace_begin(uint64_t, const char*) {}
+
+void trace_end(uint64_t) {}
+
+uint64_t GetThreadId() {
+ return syscall(__NR_gettid);
+}
+
+bool report_sysprop_change() {
+ return false;
+}
+
+} // namespace android::binder::os
+
+int __android_log_print(int /*prio*/, const char* /*tag*/, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ return 1;
+}
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index c432b3a..665dfea 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -73,6 +73,14 @@
(void)setRepr(binder, getLocalLevel(), REPR_NONE);
}
+// after deprecation of the VNDK, these should be aliases. At some point
+// all references to __ANDROID_VNDK__ should be replaced by __ANDROID_VENDOR__
+// but for right now, check that this condition holds because some
+// places check __ANDROID_VNDK__ and some places check __ANDROID_VENDOR__
+#if defined(__ANDROID_VNDK__) != defined(__ANDROID_VENDOR__)
+#error "__ANDROID_VNDK__ and __ANDROID_VENDOR__ should be aliases"
+#endif
+
Stability::Level Stability::getLocalLevel() {
#ifdef __ANDROID_APEX__
#error "APEX can't use libbinder (must use libbinder_ndk)"
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 9347ce4..dc5b1a1 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -62,8 +62,13 @@
/**
* Returns the PID of the process which has made the current binder
- * call. If not in a binder call, this will return getpid. If the
- * call is oneway, this will return 0.
+ * call. If not in a binder call, this will return getpid.
+ *
+ * Warning: oneway transactions do not receive PID. Even if you expect
+ * a transaction to be synchronous, a misbehaving client could send it
+ * as an asynchronous call and result in a 0 PID here. Additionally, if
+ * there is a race and the calling process dies, the PID may still be
+ * 0 for a synchronous call.
*/
[[nodiscard]] pid_t getCallingPid() const;
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index ddd82e8..cb44c58 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -20,8 +20,14 @@
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
#include <binder/unique_fd.h>
+
+#ifndef __TRUSTY__
#include <cutils/sockets.h>
+#endif
+
+#ifdef __linux__
#include <linux/vm_sockets.h>
+#endif // __linux__
using android::OK;
using android::RpcServer;
@@ -74,6 +80,7 @@
extern "C" {
+#ifndef __TRUSTY__
ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
auto server = RpcServer::make();
@@ -147,6 +154,7 @@
server->setRootObject(AIBinder_toPlatformBinder(service));
return createObjectHandle<ARpcServer>(server);
}
+#endif // __TRUSTY__
void ARpcServer_setSupportedFileDescriptorTransportModes(
ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[],
@@ -187,6 +195,7 @@
freeObjectHandle<RpcSession>(handle);
}
+#ifndef __TRUSTY__
AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) {
auto session = handleToStrongPointer<RpcSession>(handle);
if (status_t status = session->setupVsockClient(cid, port); status != OK) {
@@ -234,13 +243,14 @@
}
return AIBinder_fromPlatformBinder(session->getRootObject());
}
+#endif // __TRUSTY__
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
void* param) {
auto session = handleToStrongPointer<RpcSession>(handle);
auto request = [=] { return unique_fd{requestFd(param)}; };
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
- ALOGE("Failed to set up vsock client. error: %s", statusToString(status).c_str());
+ ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str());
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
diff --git a/libs/binder/liblog_stub/include/log/log.h b/libs/binder/liblog_stub/include/log/log.h
index 3b65607..91c9632 100644
--- a/libs/binder/liblog_stub/include/log/log.h
+++ b/libs/binder/liblog_stub/include/log/log.h
@@ -35,15 +35,19 @@
return ANDROID_LOG_STUB_MIN_PRIORITY <= priority;
}
-int __android_log_print(int prio, const char* tag, const char* fmt, ...)
- __attribute__((format(printf, 3, 4)))
#ifdef ANDROID_LOG_STUB_WEAK_PRINT
- __attribute__((weak))
+#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT __android_log_print
+#define __ANDROID_LOG_STUB_PRINT_ATTR __attribute__((weak))
+#else
+#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT true
+#define __ANDROID_LOG_STUB_PRINT_ATTR
#endif
- ;
+
+int __android_log_print(int prio, const char* tag, const char* fmt, ...)
+ __attribute__((format(printf, 3, 4))) __ANDROID_LOG_STUB_PRINT_ATTR;
#define IF_ALOG(priority, tag) \
- if (__android_log_stub_is_loggable(ANDROID_##priority) && __android_log_print)
+ if (__android_log_stub_is_loggable(ANDROID_##priority) && __ANDROID_LOG_STUB_IS_PRINT_PRESENT)
#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index db2d2c1..b1ab7b0 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -390,6 +390,12 @@
* calling process dies and is replaced with another process with elevated permissions and the same
* PID.
*
+ * Warning: oneway transactions do not receive PID. Even if you expect
+ * a transaction to be synchronous, a misbehaving client could send it
+ * as a synchronous call and result in a 0 PID here. Additionally, if
+ * there is a race and the calling process dies, the PID may still be
+ * 0 for a synchronous call.
+ *
* Available since API level 29.
*
* \return calling pid or the current process's PID if this thread isn't processing a transaction.
diff --git a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
index 551c59f..cb6993e 100644
--- a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
+++ b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
@@ -8,4 +8,17 @@
--allowlist-type=AIBinder_DeathRecipient
--allowlist-type=AParcel
--allowlist-type=binder_status_t
+--blocklist-function="vprintf"
+--blocklist-function="strtold"
+--blocklist-function="_vtlog"
+--blocklist-function="vscanf"
+--blocklist-function="vfprintf_worker"
+--blocklist-function="vsprintf"
+--blocklist-function="vsnprintf"
+--blocklist-function="vsnprintf_filtered"
+--blocklist-function="vfscanf"
+--blocklist-function="vsscanf"
+--blocklist-function="vdprintf"
+--blocklist-function="vasprintf"
+--blocklist-function="strtold_l"
--allowlist-function=.*
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index a957385..163f000 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -16,8 +16,10 @@
//! API for RPC Binder services.
+#[cfg(not(target_os = "trusty"))]
mod server;
mod session;
+#[cfg(not(target_os = "trusty"))]
pub use server::{RpcServer, RpcServerRef};
pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 79a9510..09688a2 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -17,11 +17,8 @@
use binder::unstable_api::new_spibinder;
use binder::{FromIBinder, SpIBinder, StatusCode, Strong};
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
-use std::ffi::CString;
-use std::os::{
- raw::{c_int, c_void},
- unix::io::{AsRawFd, BorrowedFd, RawFd},
-};
+use std::os::fd::RawFd;
+use std::os::raw::{c_int, c_void};
pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode;
@@ -87,6 +84,7 @@
}
/// Connects to an RPC Binder server over vsock for a particular interface.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_vsock_client<T: FromIBinder + ?Sized>(
&self,
cid: u32,
@@ -106,11 +104,12 @@
/// Connects to an RPC Binder server over a names Unix Domain Socket for
/// a particular interface.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>(
&self,
socket_name: &str,
) -> Result<Strong<T>, StatusCode> {
- let socket_name = match CString::new(socket_name) {
+ let socket_name = match std::ffi::CString::new(socket_name) {
Ok(s) => s,
Err(e) => {
log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
@@ -131,10 +130,12 @@
/// Connects to an RPC Binder server over a bootstrap Unix Domain Socket
/// for a particular interface.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>(
&self,
- bootstrap_fd: BorrowedFd,
+ bootstrap_fd: std::os::fd::BorrowedFd,
) -> Result<Strong<T>, StatusCode> {
+ use std::os::fd::AsRawFd;
// SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take
// ownership of bootstrap_fd. The returned AIBinder has correct
// reference count, and the ownership can safely be taken by new_spibinder.
@@ -148,12 +149,13 @@
}
/// Connects to an RPC Binder server over inet socket at the given address and port.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_inet_client<T: FromIBinder + ?Sized>(
&self,
address: &str,
port: u32,
) -> Result<Strong<T>, StatusCode> {
- let address = match CString::new(address) {
+ let address = match std::ffi::CString::new(address) {
Ok(s) => s,
Err(e) => {
log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
@@ -173,6 +175,22 @@
Self::get_interface(service)
}
+ #[cfg(target_os = "trusty")]
+ pub fn setup_trusty_client<T: FromIBinder + ?Sized>(
+ &self,
+ port: &std::ffi::CStr,
+ ) -> Result<Strong<T>, StatusCode> {
+ self.setup_preconnected_client(|| {
+ let h = tipc::Handle::connect(port)
+ .expect("Failed to connect to service port {SERVICE_PORT}");
+
+ // Do not close the handle at the end of the scope
+ let fd = h.as_raw_fd();
+ core::mem::forget(h);
+ Some(fd)
+ })
+ }
+
/// Connects to an RPC Binder server, using the given callback to get (and
/// take ownership of) file descriptors already connected to it.
pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index a3a2562..8a06274 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -101,13 +101,16 @@
/// dies and is replaced with another process with elevated permissions and
/// the same PID.
///
+ /// Warning: oneway transactions do not receive PID. Even if you expect
+ /// a transaction to be synchronous, a misbehaving client could send it
+ /// as a synchronous call and result in a 0 PID here. Additionally, if
+ /// there is a race and the calling process dies, the PID may still be
+ /// 0 for a synchronous call.
+ ///
/// Available since API level 29.
///
/// \return calling pid or the current process's PID if this thread isn't
/// processing a transaction.
- ///
- /// If the transaction being processed is a oneway transaction, then this
- /// method will return 0.
pub fn get_calling_pid() -> pid_t {
// Safety: Safe FFI
unsafe { sys::AIBinder_getCallingPid() }
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index e4d4de8..041c280 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -256,6 +256,12 @@
// contention on the device. b/276820894
test_options: {
unit_test: false,
+ test_runner_options: [
+ {
+ name: "native-test-timeout",
+ value: "10m",
+ },
+ ],
},
test_suites: ["general-tests"],
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 9869bf3..a8dabc3 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -126,8 +126,3 @@
return 1;
}
-
-// TODO(b/285204695): remove once trusty mock doesn't depend on libbase
-extern "C" int __android_log_buf_print(int, int, const char*, const char*, ...) {
- return -ENOSYS;
-}
diff --git a/libs/binder/trusty/binder_rpc_unstable/rules.mk b/libs/binder/trusty/binder_rpc_unstable/rules.mk
new file mode 100644
index 0000000..d8dbce5
--- /dev/null
+++ b/libs/binder/trusty/binder_rpc_unstable/rules.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := \
+ $(LIBBINDER_DIR)/libbinder_rpc_unstable.cpp \
+
+MODULE_EXPORT_INCLUDES += \
+ $(LIBBINDER_DIR)/include_rpc_unstable \
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ trusty/user/base/lib/libstdc++-trusty \
+
+include make/library.mk
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 788184d..5cbe0af 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -69,6 +69,7 @@
-DBINDER_DISABLE_NATIVE_HANDLE \
-DBINDER_DISABLE_BLOB \
-DBINDER_NO_LIBBASE \
+ -D__ANDROID_VENDOR__ \
-D__ANDROID_VNDK__ \
MODULE_DEPS += \
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index e0f821f..f2f140d 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -72,6 +72,7 @@
-DBINDER_DISABLE_NATIVE_HANDLE \
-DBINDER_DISABLE_BLOB \
-DBINDER_NO_LIBBASE \
+ -D__ANDROID_VENDOR__ \
-D__ANDROID_VNDK__ \
# libbinder has some deprecated declarations that we want to produce warnings
diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp
new file mode 100644
index 0000000..6f96566
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder_rpc_unstable.hpp>
diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs
new file mode 100644
index 0000000..c7036f4
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Generated Rust bindings to binder_rpc_unstable
+
+#[allow(bad_style)]
+mod sys {
+ include!(env!("BINDGEN_INC_FILE"));
+}
+
+pub use sys::*;
diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
new file mode 100644
index 0000000..ef1b7c3
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
@@ -0,0 +1,40 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LOCAL_DIR)/lib.rs
+
+MODULE_CRATE_NAME := binder_rpc_unstable_bindgen
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/binder_rpc_unstable \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
+ trusty/user/base/lib/libstdc++-trusty \
+ trusty/user/base/lib/trusty-sys \
+
+MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp
+
+MODULE_BINDGEN_FLAGS += \
+ --blocklist-type="AIBinder" \
+ --raw-line="use binder_ndk_sys::AIBinder;" \
+ --rustified-enum="ARpcSession_FileDescriptorTransportMode" \
+
+include make/library.mk
diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk
new file mode 100644
index 0000000..76f3b94
--- /dev/null
+++ b/libs/binder/trusty/rust/rpcbinder/rules.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LIBBINDER_DIR)/rust/rpcbinder/src/lib.rs
+
+MODULE_CRATE_NAME := rpcbinder
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ $(LIBBINDER_DIR)/trusty/rust \
+ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
+ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
+ external/rust/crates/foreign-types \
+ trusty/user/base/lib/tipc/rust \
+ trusty/user/base/lib/trusty-sys \
+
+include make/library.mk
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index be90df3..6de7eb5 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -26,6 +26,7 @@
$(LIBBINDER_DIR)/trusty \
$(LIBBINDER_DIR)/trusty/ndk \
$(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
+ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
external/rust/crates/downcast-rs \
trusty/user/base/lib/trusty-sys \
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index ea1b5e4..918680d 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -17,6 +17,7 @@
"enum_test.cpp",
"fake_guard_test.cpp",
"flags_test.cpp",
+ "function_test.cpp",
"future_test.cpp",
"match_test.cpp",
"mixins_test.cpp",
diff --git a/libs/ftl/function_test.cpp b/libs/ftl/function_test.cpp
new file mode 100644
index 0000000..91b5e08
--- /dev/null
+++ b/libs/ftl/function_test.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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/function.h>
+#include <gtest/gtest.h>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+#include <type_traits>
+
+namespace android::test {
+namespace {
+
+// Create an alias to composite requirements defined by the trait class `T` for easier testing.
+template <typename T, typename S>
+inline constexpr bool is_opaquely_storable = (T::template require_trivially_copyable<S> &&
+ T::template require_trivially_destructible<S> &&
+ T::template require_will_fit_in_opaque_storage<S> &&
+ T::template require_alignment_compatible<S>);
+
+// `I` gives a count of sizeof(std::intptr_t) bytes , and `J` gives a raw count of bytes
+template <size_t I, size_t J = 0>
+struct KnownSizeFunctionObject {
+ using Data = std::array<std::byte, sizeof(std::intptr_t) * I + J>;
+ void operator()() const {};
+ Data data{};
+};
+
+} // namespace
+
+// static_assert the expected type traits
+static_assert(std::is_invocable_r_v<void, ftl::Function<void()>>);
+static_assert(std::is_trivially_copyable_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_destructible_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_move_constructible_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_move_assignable_v<ftl::Function<void()>>);
+
+template <typename T>
+using function_traits = ftl::details::function_traits<T>;
+
+// static_assert that the expected value of N is used for known function object sizes.
+static_assert(function_traits<KnownSizeFunctionObject<0, 0>>::size == 0);
+static_assert(function_traits<KnownSizeFunctionObject<0, 1>>::size == 0);
+static_assert(function_traits<KnownSizeFunctionObject<1, 0>>::size == 0);
+static_assert(function_traits<KnownSizeFunctionObject<1, 1>>::size == 1);
+static_assert(function_traits<KnownSizeFunctionObject<2, 0>>::size == 1);
+static_assert(function_traits<KnownSizeFunctionObject<2, 1>>::size == 2);
+
+// Check that is_function_v works
+static_assert(!ftl::is_function_v<KnownSizeFunctionObject<0>>);
+static_assert(!ftl::is_function_v<std::function<void()>>);
+static_assert(ftl::is_function_v<ftl::Function<void()>>);
+
+// static_assert what can and cannot be stored inside the opaque storage
+
+template <size_t N>
+using function_opaque_storage = ftl::details::function_opaque_storage<N>;
+
+// Function objects can be stored if they fit.
+static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<0>>);
+static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<1>>);
+static_assert(!is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<2>>);
+
+static_assert(is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<2>>);
+static_assert(!is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<3>>);
+
+static_assert(is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<3>>);
+static_assert(!is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<4>>);
+
+// Another opaque storage can be stored if it fits. This property is used to copy smaller
+// ftl::Functions into larger ones.
+static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<0>::type>);
+static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<1>::type>);
+static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<2>::type>);
+static_assert(!is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<3>::type>);
+
+// Function objects that aren't trivially copyable or destroyable cannot be stored.
+auto lambda_capturing_unique_ptr = [ptr = std::unique_ptr<void*>()] { static_cast<void>(ptr); };
+static_assert(
+ !is_opaquely_storable<function_opaque_storage<2>, decltype(lambda_capturing_unique_ptr)>);
+
+// Keep in sync with "Example usage" in header file.
+TEST(Function, Example) {
+ using namespace std::string_view_literals;
+
+ class MyClass {
+ public:
+ void on_event() const {}
+ int on_string(int*, std::string_view) { return 1; }
+
+ auto get_function() {
+ return ftl::make_function([this] { on_event(); });
+ }
+ } cls;
+
+ // A function container with no arguments, and returning no value.
+ ftl::Function<void()> f;
+
+ // Construct a ftl::Function containing a small lambda.
+ f = cls.get_function();
+
+ // Construct a ftl::Function that calls `cls.on_event()`.
+ f = ftl::make_function<&MyClass::on_event>(&cls);
+
+ // Create a do-nothing function.
+ f = ftl::no_op;
+
+ // Invoke the contained function.
+ f();
+
+ // Also invokes it.
+ std::invoke(f);
+
+ // Create a typedef to give a more meaningful name and bound the size.
+ using MyFunction = ftl::Function<int(std::string_view), 2>;
+ int* ptr = nullptr;
+ auto f1 =
+ MyFunction::make([cls = &cls, ptr](std::string_view sv) { return cls->on_string(ptr, sv); });
+ int r = f1("abc"sv);
+
+ // Returns a default-constructed int (0).
+ f1 = ftl::no_op;
+ r = f1("abc"sv);
+ EXPECT_EQ(r, 0);
+}
+
+TEST(Function, BasicOperations) {
+ // Default constructible.
+ ftl::Function<int()> f;
+
+ // Compares as empty
+ EXPECT_FALSE(f);
+ EXPECT_TRUE(f == nullptr);
+ EXPECT_FALSE(f != nullptr);
+ EXPECT_TRUE(ftl::Function<int()>() == f);
+ EXPECT_FALSE(ftl::Function<int()>() != f);
+
+ // Assigning no_op sets it to not empty.
+ f = ftl::no_op;
+
+ // Verify it can be called, and that it returns a default constructed value.
+ EXPECT_EQ(f(), 0);
+
+ // Comparable when non-empty.
+ EXPECT_TRUE(f);
+ EXPECT_FALSE(f == nullptr);
+ EXPECT_TRUE(f != nullptr);
+ EXPECT_FALSE(ftl::Function<int()>() == f);
+ EXPECT_TRUE(ftl::Function<int()>() != f);
+
+ // Constructing from nullptr means empty.
+ f = ftl::Function<int()>{nullptr};
+ EXPECT_FALSE(f);
+
+ // Assigning nullptr means it is empty.
+ f = nullptr;
+ EXPECT_FALSE(f);
+
+ // Move construction
+ f = ftl::no_op;
+ ftl::Function<int()> g{std::move(f)};
+ EXPECT_TRUE(g != nullptr);
+
+ // Move assignment
+ f = nullptr;
+ f = std::move(g);
+ EXPECT_TRUE(f != nullptr);
+
+ // Copy construction
+ ftl::Function<int()> h{f};
+ EXPECT_TRUE(h != nullptr);
+
+ // Copy assignment
+ g = h;
+ EXPECT_TRUE(g != nullptr);
+}
+
+TEST(Function, CanMoveConstructFromLambda) {
+ auto lambda = [] {};
+ ftl::Function<void()> f{std::move(lambda)};
+}
+
+TEST(Function, TerseDeducedConstructAndAssignFromLambda) {
+ auto f = ftl::Function([] { return 1; });
+ EXPECT_EQ(f(), 1);
+
+ f = [] { return 2; };
+ EXPECT_EQ(f(), 2);
+}
+
+namespace {
+
+struct ImplicitConversionsHelper {
+ auto exact(int) -> int { return 0; }
+ auto inexact(long) -> short { return 0; }
+ // TODO: Switch to `auto templated(auto x)` with C++20
+ template <typename T>
+ T templated(T x) {
+ return x;
+ }
+
+ static auto static_exact(int) -> int { return 0; }
+ static auto static_inexact(long) -> short { return 0; }
+ // TODO: Switch to `static auto static_templated(auto x)` with C++20
+ template <typename T>
+ static T static_templated(T x) {
+ return x;
+ }
+};
+
+} // namespace
+
+TEST(Function, ImplicitConversions) {
+ using Function = ftl::Function<int(int)>;
+ auto check = [](Function f) { return f(0); };
+ auto exact = [](int) -> int { return 0; };
+ auto inexact = [](long) -> short { return 0; };
+ auto templated = [](auto x) { return x; };
+
+ ImplicitConversionsHelper helper;
+
+ // Note, `check(nullptr)` would crash, so we can only check if it would be invocable.
+ static_assert(std::is_invocable_v<decltype(check), decltype(nullptr)>);
+
+ // Note: We invoke each of these to fully expand all the templates involved.
+ EXPECT_EQ(check(ftl::no_op), 0);
+
+ EXPECT_EQ(check(exact), 0);
+ EXPECT_EQ(check(inexact), 0);
+ EXPECT_EQ(check(templated), 0);
+
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::exact>(&helper)), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::inexact>(&helper)), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::templated<int>>(&helper)), 0);
+
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_exact>()), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_inexact>()), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_templated<int>>()), 0);
+}
+
+TEST(Function, MakeWithNonConstMemberFunction) {
+ struct Observer {
+ bool called = false;
+ void setCalled() { called = true; }
+ } observer;
+
+ auto f = ftl::make_function<&Observer::setCalled>(&observer);
+
+ f();
+
+ EXPECT_TRUE(observer.called);
+
+ EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
+}
+
+TEST(Function, MakeWithConstMemberFunction) {
+ struct Observer {
+ mutable bool called = false;
+ void setCalled() const { called = true; }
+ } observer;
+
+ const auto f = ftl::make_function<&Observer::setCalled>(&observer);
+
+ f();
+
+ EXPECT_TRUE(observer.called);
+
+ EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
+}
+
+TEST(Function, MakeWithConstClassPointer) {
+ const struct Observer {
+ mutable bool called = false;
+ void setCalled() const { called = true; }
+ } observer;
+
+ const auto f = ftl::make_function<&Observer::setCalled>(&observer);
+
+ f();
+
+ EXPECT_TRUE(observer.called);
+
+ EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
+}
+
+TEST(Function, MakeWithNonCapturingLambda) {
+ auto f = ftl::make_function([](int a, int b) { return a + b; });
+ EXPECT_EQ(f(1, 2), 3);
+}
+
+TEST(Function, MakeWithCapturingLambda) {
+ bool called = false;
+ auto f = ftl::make_function([&called](int a, int b) {
+ called = true;
+ return a + b;
+ });
+ EXPECT_EQ(f(1, 2), 3);
+ EXPECT_TRUE(called);
+}
+
+TEST(Function, MakeWithCapturingMutableLambda) {
+ bool called = false;
+ auto f = ftl::make_function([&called](int a, int b) mutable {
+ called = true;
+ return a + b;
+ });
+ EXPECT_EQ(f(1, 2), 3);
+ EXPECT_TRUE(called);
+}
+
+TEST(Function, MakeWithThreePointerCapturingLambda) {
+ bool my_bool = false;
+ int my_int = 0;
+ float my_float = 0.f;
+
+ auto f = ftl::make_function(
+ [ptr_bool = &my_bool, ptr_int = &my_int, ptr_float = &my_float](int a, int b) mutable {
+ *ptr_bool = true;
+ *ptr_int = 1;
+ *ptr_float = 1.f;
+
+ return a + b;
+ });
+
+ EXPECT_EQ(f(1, 2), 3);
+
+ EXPECT_TRUE(my_bool);
+ EXPECT_EQ(my_int, 1);
+ EXPECT_EQ(my_float, 1.f);
+}
+
+TEST(Function, MakeWithFreeFunction) {
+ auto f = ftl::make_function<&std::make_unique<int, int>>();
+ std::unique_ptr<int> unique_int = f(1);
+ ASSERT_TRUE(unique_int);
+ EXPECT_EQ(*unique_int, 1);
+}
+
+TEST(Function, CopyToLarger) {
+ int counter = 0;
+ ftl::Function<void()> a{[ptr_counter = &counter] { (*ptr_counter)++; }};
+ ftl::Function<void(), 1> b = a;
+ ftl::Function<void(), 2> c = a;
+
+ EXPECT_EQ(counter, 0);
+ a();
+ EXPECT_EQ(counter, 1);
+ b();
+ EXPECT_EQ(counter, 2);
+ c();
+ EXPECT_EQ(counter, 3);
+
+ b = [ptr_counter = &counter] { (*ptr_counter) += 2; };
+ c = [ptr_counter = &counter] { (*ptr_counter) += 3; };
+
+ b();
+ EXPECT_EQ(counter, 5);
+ c();
+ EXPECT_EQ(counter, 8);
+}
+
+} // namespace android::test
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 44a9e52..eb4d3df 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -423,7 +423,6 @@
"libhidlbase",
"liblog",
"libnativewindow",
- "libselinux",
"libsync",
"libui",
"libutils",
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 744201a..11f5174 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -38,43 +38,10 @@
#include <private/gui/BufferQueueThreadState.h>
#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
#include <binder/PermissionCache.h>
-#include <selinux/android.h>
-#include <selinux/selinux.h>
#endif
#include <system/window.h>
-namespace {
-#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
-int selinux_log_suppress_callback(int, const char*, ...) { // NOLINT
- // DO NOTHING
- return 0;
-}
-
-bool hasAccessToPermissionService() {
- char* ctx;
-
- if (getcon(&ctx) == -1) {
- // Failed to get current selinux context
- return false;
- }
-
- union selinux_callback cb;
-
- cb.func_log = selinux_log_suppress_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
-
- bool hasAccess = selinux_check_access(ctx, "u:object_r:permission_service:s0",
- "service_manager", "find", NULL) == 0;
- freecon(ctx);
- cb.func_log = hasAccess ? selinux_log_callback : selinux_vendor_log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
-
- return hasAccess;
-}
-#endif
-} // namespace
-
namespace android {
// Macros for include BufferQueueCore information in log messages
@@ -843,18 +810,14 @@
const uid_t uid = BufferQueueThreadState::getCallingUid();
#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
// permission check can't be done for vendors as vendors have no access to
- // the PermissionController. We need to do a runtime check as well, since
- // the system variant of libgui can be loaded in a vendor process. For eg:
- // if a HAL uses an llndk library that depends on libgui (libmediandk etc).
- if (hasAccessToPermissionService()) {
- const pid_t pid = BufferQueueThreadState::getCallingPid();
- if ((uid != shellUid) &&
- !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
- outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
- "from pid=%d, uid=%d\n",
- pid, uid);
- denied = true;
- }
+ // the PermissionController.
+ const pid_t pid = BufferQueueThreadState::getCallingPid();
+ if ((uid != shellUid) &&
+ !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
+ outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+ "from pid=%d, uid=%d\n",
+ pid, uid);
+ denied = true;
}
#else
if (uid != shellUid) {
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 93df124..c6f3363 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -344,6 +344,10 @@
handleRefreshRateUpdates();
}
+void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId, int32_t, int32_t) {
+ LOG_ALWAYS_FATAL("dispatchHdcpLevelsChanged was called but was never registered");
+}
+
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
case MSG_SCHEDULE_CALLBACKS:
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 5dd058c..f3de96d 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -195,6 +195,11 @@
dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId,
std::move(mFrameRateOverrides));
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ dispatchHdcpLevelsChanged(ev.header.displayId,
+ ev.hdcpLevelsChange.connectedLevel,
+ ev.hdcpLevelsChange.maxLevel);
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 922b0dd..8d18551 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2782,9 +2782,16 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) {
+status_t SurfaceComposerClient::setGameModeFrameRateOverride(uid_t uid, float frameRate) {
binder::Status status =
- ComposerServiceAIDL::getComposerService()->setOverrideFrameRate(uid, frameRate);
+ ComposerServiceAIDL::getComposerService()->setGameModeFrameRateOverride(uid, frameRate);
+ return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::setGameDefaultFrameRateOverride(uid_t uid, float frameRate) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->setGameDefaultFrameRateOverride(uid,
+ frameRate);
return statusTFromBinderStatus(status);
}
@@ -3110,7 +3117,6 @@
->removeWindowInfosListener(windowInfosListener,
ComposerServiceAIDL::getComposerService());
}
-
// ----------------------------------------------------------------------------
status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 6a4460b..ba1d196 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -26,6 +26,41 @@
namespace android::gui {
+namespace {
+
+std::ostream& operator<<(std::ostream& out, const sp<IBinder>& binder) {
+ if (binder == nullptr) {
+ out << "<null>";
+ } else {
+ out << binder.get();
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Region& region) {
+ if (region.isEmpty()) {
+ out << "<empty>";
+ return out;
+ }
+
+ bool first = true;
+ Region::const_iterator cur = region.begin();
+ Region::const_iterator const tail = region.end();
+ while (cur != tail) {
+ if (first) {
+ first = false;
+ } else {
+ out << "|";
+ }
+ out << "[" << cur->left << "," << cur->top << "][" << cur->right << "," << cur->bottom
+ << "]";
+ cur++;
+ }
+ return out;
+}
+
+} // namespace
+
void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) {
if (value) {
inputConfig |= config;
@@ -222,4 +257,24 @@
void WindowInfoHandle::updateFrom(sp<WindowInfoHandle> handle) {
mInfo = handle->mInfo;
}
+
+std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) {
+ const WindowInfo& info = *window.getInfo();
+ std::string transform;
+ info.transform.dump(transform, "transform", " ");
+ out << "name=" << info.name << ", id=" << info.id << ", displayId=" << info.displayId
+ << ", inputConfig=" << info.inputConfig.string() << ", alpha=" << info.alpha << ", frame=["
+ << info.frame.left << "," << info.frame.top << "][" << info.frame.right << ","
+ << info.frame.bottom << "], globalScale=" << info.globalScaleFactor
+ << ", applicationInfo.name=" << info.applicationInfo.name
+ << ", applicationInfo.token=" << info.applicationInfo.token
+ << ", touchableRegion=" << info.touchableRegion << ", ownerPid=" << info.ownerPid.toString()
+ << ", ownerUid=" << info.ownerUid.toString() << ", dispatchingTimeout="
+ << std::chrono::duration_cast<std::chrono::milliseconds>(info.dispatchingTimeout).count()
+ << "ms, token=" << info.token.get()
+ << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode) << "\n"
+ << transform;
+ return out;
+}
+
} // namespace android::gui
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index d24f8ee..e3122bc 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -477,10 +477,21 @@
/**
* Set the override frame rate for a specified uid by GameManagerService.
+ * This override is controlled by game mode interventions.
+ * Passing the frame rate and uid to SurfaceFlinger to update the override mapping
+ * in the LayerHistory.
+ */
+ void setGameModeFrameRateOverride(int uid, float frameRate);
+
+ /**
+ * Set the override frame rate for a specified uid by GameManagerService.
+ * This override is controlled by game default frame rate sysprop:
+ * "ro.surface_flinger.game_default_frame_rate_override" holding the override value,
+ * "persisit.graphics.game_default_frame_rate.enabled" to determine if it's enabled.
* Passing the frame rate and uid to SurfaceFlinger to update the override mapping
* in the scheduler.
*/
- void setOverrideFrameRate(int uid, float frameRate);
+ void setGameDefaultFrameRateOverride(int uid, float frameRate);
oneway void updateSmallAreaDetection(in int[] appIds, in float[] thresholds);
diff --git a/libs/gui/android/gui/TouchOcclusionMode.aidl b/libs/gui/android/gui/TouchOcclusionMode.aidl
index d91d052..ed72105 100644
--- a/libs/gui/android/gui/TouchOcclusionMode.aidl
+++ b/libs/gui/android/gui/TouchOcclusionMode.aidl
@@ -43,5 +43,6 @@
* The window won't count for touch occlusion rules if the touch passes
* through it.
*/
- ALLOW
+ ALLOW,
+ ftl_last=ALLOW,
}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index ffe7e41..c952ba2 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -149,7 +149,8 @@
(const gui::Color&, const gui::Color&, float, float, float), (override));
MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
(const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
- MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
+ MOCK_METHOD(binder::Status, setGameModeFrameRateOverride, (int32_t, float), (override));
+ MOCK_METHOD(binder::Status, setGameDefaultFrameRateOverride, (int32_t, float), (override));
MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override));
MOCK_METHOD(binder::Status, setDebugFlash, (int), (override));
MOCK_METHOD(binder::Status, scheduleComposite, (), (override));
@@ -206,6 +207,7 @@
MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId));
MOCK_METHOD3(dispatchFrameRateOverrides,
void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>));
+ MOCK_METHOD3(dispatchHdcpLevelsChanged, void(PhysicalDisplayId, int32_t, int32_t));
};
} // namespace android
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 9fef512..55a7aa7 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -116,6 +116,8 @@
void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
+ void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) override;
void scheduleCallbacks();
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index fe2dd20..82cd50c 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -65,6 +65,9 @@
virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) = 0;
+ virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) = 0;
+
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 79582ce..8c1103b 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -58,7 +58,6 @@
// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
-
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -66,6 +65,7 @@
DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
+ DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'),
};
struct Event {
@@ -101,12 +101,22 @@
float frameRateHz __attribute__((aligned(8)));
};
+ /*
+ * The values are defined in aidl:
+ * hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl
+ */
+ struct HdcpLevelsChange {
+ int32_t connectedLevel;
+ int32_t maxLevel;
+ };
+
Header header;
union {
VSync vsync;
Hotplug hotplug;
ModeChange modeChange;
FrameRateOverride frameRateOverride;
+ HdcpLevelsChange hdcpLevelsChange;
};
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 5bf6c47..14e3dd5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -201,7 +201,13 @@
// Sets the frame rate of a particular app (uid). This is currently called
// by GameManager.
- static status_t setOverrideFrameRate(uid_t uid, float frameRate);
+ static status_t setGameModeFrameRateOverride(uid_t uid, float frameRate);
+
+ // Sets the frame rate of a particular app (uid). This is currently called
+ // by GameManager and controlled by two sysprops:
+ // "ro.surface_flinger.game_default_frame_rate_override" holding the override value,
+ // "persisit.graphics.game_default_frame_rate.enabled" to determine if it's enabled.
+ static status_t setGameDefaultFrameRateOverride(uid_t uid, float frameRate);
// Update the small area detection whole appId-threshold mappings by same size appId and
// threshold vector.
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index dcc38d7..b72b71a 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -176,6 +176,8 @@
static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS),
CLONE =
static_cast<uint32_t>(os::InputConfig::CLONE),
+ GLOBAL_STYLUS_BLOCKS_TOUCH =
+ static_cast<uint32_t>(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH),
// clang-format on
};
@@ -315,4 +317,7 @@
WindowInfo mInfo;
};
+
+std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window);
+
} // namespace android::gui
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 60221aa..c6ea317 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -921,7 +921,11 @@
return binder::Status::ok();
}
- binder::Status setOverrideFrameRate(int32_t /*uid*/, float /*frameRate*/) override {
+ binder::Status setGameModeFrameRateOverride(int32_t /*uid*/, float /*frameRate*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status setGameDefaultFrameRateOverride(int32_t /*uid*/, float /*frameRate*/) override {
return binder::Status::ok();
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index bd5b67b..8eaff00 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -379,6 +379,11 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const PointerProperties& properties) {
+ out << "Pointer(id=" << properties.id << ", " << ftl::enum_string(properties.toolType) << ")";
+ return out;
+}
+
// --- PointerCoords ---
float PointerCoords::getAxisValue(int32_t axis) const {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 09e98d0..669f801 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -646,7 +646,7 @@
"action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32 " \n%s",
+ "pointerCount=%" PRIu32 "\n%s",
mChannel->getName().c_str(), __func__, seq, eventId, deviceId,
inputEventSourceToString(source).c_str(), displayId,
MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags,
diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl
index 4e644ff..5d39155 100644
--- a/libs/input/android/os/InputConfig.aidl
+++ b/libs/input/android/os/InputConfig.aidl
@@ -150,4 +150,11 @@
* likely a duplicate window with the same client token, but different bounds.
*/
CLONE = 1 << 16,
+
+ /**
+ * If the stylus is currently down *anywhere* on the screen, new touches will not be delivered
+ * to the window with this flag. This helps prevent unexpected clicks on some system windows,
+ * like StatusBar and TaskBar.
+ */
+ GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17,
}
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e7b2195..745b6f3 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -113,6 +113,22 @@
AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
"HAL and AHardwareBuffer pixel format don't match");
+static enum AHardwareBufferStatus filterStatus(status_t status) {
+ switch (status) {
+ case STATUS_OK:
+ return AHARDWAREBUFFER_STATUS_OK;
+ case STATUS_NO_MEMORY:
+ return AHARDWAREBUFFER_STATUS_NO_MEMORY;
+ case STATUS_BAD_VALUE:
+ return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ case STATUS_UNKNOWN_TRANSACTION:
+ case STATUS_INVALID_OPERATION:
+ return AHARDWAREBUFFER_STATUS_UNSUPPORTED;
+ default:
+ return AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR;
+ }
+}
+
// ----------------------------------------------------------------------------
// Public functions
// ----------------------------------------------------------------------------
@@ -511,6 +527,24 @@
return AParcel_viewPlatformParcel(parcel)->write(*gb);
}
+ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) {
+ const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ if (!gb) return ADATASPACE_UNKNOWN;
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+ status_t status = gb->getDataspace(&dataspace);
+ if (status != OK) {
+ return ADATASPACE_UNKNOWN;
+ }
+ return static_cast<ADataSpace>(dataspace);
+}
+
+enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* buffer,
+ ADataSpace dataspace) {
+ GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ auto& mapper = GraphicBufferMapper::get();
+ return filterStatus(mapper.setDataspace(gb->handle, static_cast<ui::Dataspace>(dataspace)));
+}
+
// ----------------------------------------------------------------------------
// VNDK functions
// ----------------------------------------------------------------------------
@@ -552,6 +586,56 @@
return NO_ERROR;
}
+enum AHardwareBufferStatus AHardwareBuffer_allocate2(
+ const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions,
+ size_t additionalOptionsSize, AHardwareBuffer** outBuffer) {
+ (void)additionalOptions;
+ (void)additionalOptionsSize;
+ if (!outBuffer || !desc) return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) {
+ return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ }
+
+ int format = AHardwareBuffer_convertToPixelFormat(desc->format);
+ uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+
+ std::vector<GraphicBufferAllocator::AdditionalOptions> extras;
+ extras.reserve(additionalOptionsSize);
+ for (size_t i = 0; i < additionalOptionsSize; i++) {
+ extras.push_back(GraphicBufferAllocator::AdditionalOptions{additionalOptions[i].name,
+ additionalOptions[i].value});
+ }
+
+ const auto extrasCount = extras.size();
+ auto gbuffer = sp<GraphicBuffer>::make(GraphicBufferAllocator::AllocationRequest{
+ .importBuffer = true,
+ .width = desc->width,
+ .height = desc->height,
+ .format = format,
+ .layerCount = desc->layers,
+ .usage = usage,
+ .requestorName = std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]",
+ .extras = std::move(extras),
+ });
+
+ status_t err = gbuffer->initCheck();
+ if (err != 0 || gbuffer->handle == nullptr) {
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
+ ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u, extrasCount=%zd) failed (%s), handle=%p",
+ desc->width, desc->height, desc->layers, extrasCount, strerror(-err),
+ gbuffer->handle);
+ return filterStatus(err == 0 ? UNKNOWN_ERROR : err);
+ }
+
+ *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
+
+ // Ensure the buffer doesn't get destroyed when the sp<> goes away.
+ AHardwareBuffer_acquire(*outBuffer);
+ return AHARDWAREBUFFER_STATUS_OK;
+}
+
// ----------------------------------------------------------------------------
// Helpers implementation
// ----------------------------------------------------------------------------
@@ -652,12 +736,9 @@
return ahardwarebuffer_format;
}
+// TODO: Remove, this is just to make an overly aggressive ABI checker happy
int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) {
- GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
- auto& mapper = GraphicBufferMapper::get();
- ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
- mapper.getDataspace(gb->handle, &dataspace);
- return static_cast<int32_t>(dataspace);
+ return ::AHardwareBuffer_getDataSpace(buffer);
}
uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index dd5958d..f97eed5 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -152,31 +152,56 @@
int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) {
static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
- static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
- static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
- static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
- static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
- static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
- static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
- static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
- static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
- static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
- static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
- static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
- static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
- static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
- static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
- static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
- static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
- static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
- static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK));
- static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
- static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL));
- static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
- static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_MASK) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT709) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625_UNADJUSTED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525_UNADJUSTED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT470M) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_FILM) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_DCI_P3) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_ADOBE_RGB) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_MASK) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_LINEAR) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_SMPTE_170M) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_2) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_6) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_8) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_ST2084) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_HLG) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_MASK) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_MASK));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_FULL) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_FULL));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_LIMITED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_EXTENDED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 880c694..f145a2f 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -27,8 +27,8 @@
#include <stdint.h>
-struct AHardwareBuffer;
-struct AHardwareBuffer_Desc;
+#include <vndk/hardware_buffer.h>
+
struct ANativeWindowBuffer;
namespace android {
@@ -46,11 +46,6 @@
// convert HAL format to AHardwareBuffer format (note: this is a no-op)
uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
-// retrieves a dataspace from the AHardwareBuffer metadata, if the device
-// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not
-// supported.
-int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer);
-
// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index eab21fb..d4278be 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -37,7 +37,7 @@
/**
* ADataSpace.
*/
-enum ADataSpace {
+enum ADataSpace : int32_t {
/**
* Default-assumption data space, when not explicitly specified.
*
@@ -63,7 +63,7 @@
* Defines the chromaticity coordinates of the source primaries in terms of
* the CIE 1931 definition of x and y specified in ISO 11664-1.
*/
- STANDARD_MASK = 63 << 16,
+ ADATASPACE_STANDARD_MASK = 63 << 16,
/**
* Chromacity coordinates are unknown or are determined by the application.
@@ -78,7 +78,7 @@
* For all other formats standard is undefined, and implementations should use
* an appropriate standard for the data represented.
*/
- STANDARD_UNSPECIFIED = 0 << 16,
+ ADATASPACE_STANDARD_UNSPECIFIED = 0 << 16,
/**
* <pre>
@@ -91,7 +91,7 @@
* Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT709 = 1 << 16,
+ ADATASPACE_STANDARD_BT709 = 1 << 16,
/**
* <pre>
@@ -106,7 +106,7 @@
* to minimize the color shift into RGB space that uses BT.709
* primaries.
*/
- STANDARD_BT601_625 = 2 << 16,
+ ADATASPACE_STANDARD_BT601_625 = 2 << 16,
/**
* <pre>
@@ -119,7 +119,7 @@
* Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT601_625_UNADJUSTED = 3 << 16,
+ ADATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << 16,
/**
* <pre>
@@ -134,7 +134,7 @@
* to minimize the color shift into RGB space that uses BT.709
* primaries.
*/
- STANDARD_BT601_525 = 4 << 16,
+ ADATASPACE_STANDARD_BT601_525 = 4 << 16,
/**
* <pre>
@@ -147,7 +147,7 @@
* Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
* for RGB conversion (as in SMPTE 240M).
*/
- STANDARD_BT601_525_UNADJUSTED = 5 << 16,
+ ADATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << 16,
/**
* <pre>
@@ -160,7 +160,7 @@
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT2020 = 6 << 16,
+ ADATASPACE_STANDARD_BT2020 = 6 << 16,
/**
* <pre>
@@ -173,7 +173,7 @@
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion using the linear domain.
*/
- STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
+ ADATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
/**
* <pre>
@@ -186,7 +186,7 @@
* Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT470M = 8 << 16,
+ ADATASPACE_STANDARD_BT470M = 8 << 16,
/**
* <pre>
@@ -199,7 +199,7 @@
* Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
* for RGB conversion.
*/
- STANDARD_FILM = 9 << 16,
+ ADATASPACE_STANDARD_FILM = 9 << 16,
/**
* SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
@@ -210,7 +210,7 @@
* red 0.680 0.320
* white (D65) 0.3127 0.3290</pre>
*/
- STANDARD_DCI_P3 = 10 << 16,
+ ADATASPACE_STANDARD_DCI_P3 = 10 << 16,
/**
* Adobe RGB
@@ -221,7 +221,7 @@
* red 0.640 0.330
* white (D65) 0.3127 0.3290</pre>
*/
- STANDARD_ADOBE_RGB = 11 << 16,
+ ADATASPACE_STANDARD_ADOBE_RGB = 11 << 16,
/**
* Transfer aspect
@@ -236,7 +236,7 @@
* component. Implementation may apply the transfer function in RGB space
* for all pixel formats if desired.
*/
- TRANSFER_MASK = 31 << 22,
+ ADATASPACE_TRANSFER_MASK = 31 << 22,
/**
* Transfer characteristics are unknown or are determined by the
@@ -244,13 +244,13 @@
*
* Implementations should use the following transfer functions:
*
- * For YCbCr formats: use TRANSFER_SMPTE_170M
- * For RGB formats: use TRANSFER_SRGB
+ * For YCbCr formats: use ADATASPACE_TRANSFER_SMPTE_170M
+ * For RGB formats: use ADATASPACE_TRANSFER_SRGB
*
* For all other formats transfer function is undefined, and implementations
* should use an appropriate standard for the data represented.
*/
- TRANSFER_UNSPECIFIED = 0 << 22,
+ ADATASPACE_TRANSFER_UNSPECIFIED = 0 << 22,
/**
* Linear transfer.
@@ -260,7 +260,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_LINEAR = 1 << 22,
+ ADATASPACE_TRANSFER_LINEAR = 1 << 22,
/**
* sRGB transfer.
@@ -271,7 +271,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_SRGB = 2 << 22,
+ ADATASPACE_TRANSFER_SRGB = 2 << 22,
/**
* SMPTE 170M transfer.
@@ -282,7 +282,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_SMPTE_170M = 3 << 22,
+ ADATASPACE_TRANSFER_SMPTE_170M = 3 << 22,
/**
* Display gamma 2.2.
@@ -292,7 +292,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_2 = 4 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_2 = 4 << 22,
/**
* Display gamma 2.6.
@@ -302,7 +302,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_6 = 5 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_6 = 5 << 22,
/**
* Display gamma 2.8.
@@ -312,7 +312,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_8 = 6 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_8 = 6 << 22,
/**
* SMPTE ST 2084 (Dolby Perceptual Quantizer).
@@ -328,7 +328,7 @@
* L = 1 corresponds to 10000 cd/m2
* E - corresponding electrical signal</pre>
*/
- TRANSFER_ST2084 = 7 << 22,
+ ADATASPACE_TRANSFER_ST2084 = 7 << 22,
/**
* ARIB STD-B67 Hybrid Log Gamma.
@@ -344,7 +344,7 @@
* to reference white level of 100 cd/m2
* E - corresponding electrical signal</pre>
*/
- TRANSFER_HLG = 8 << 22,
+ ADATASPACE_TRANSFER_HLG = 8 << 22,
/**
* Range aspect
@@ -352,7 +352,7 @@
* Defines the range of values corresponding to the unit range of 0-1.
* This is defined for YCbCr only, but can be expanded to RGB space.
*/
- RANGE_MASK = 7 << 27,
+ ADATASPACE_RANGE_MASK = 7 << 27,
/**
* Range is unknown or are determined by the application. Implementations
@@ -365,13 +365,13 @@
* For all other formats range is undefined, and implementations should use
* an appropriate range for the data represented.
*/
- RANGE_UNSPECIFIED = 0 << 27,
+ ADATASPACE_RANGE_UNSPECIFIED = 0 << 27,
/**
* Full range uses all values for Y, Cb and Cr from
* 0 to 2^b-1, where b is the bit depth of the color format.
*/
- RANGE_FULL = 1 << 27,
+ ADATASPACE_RANGE_FULL = 1 << 27,
/**
* Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
@@ -386,7 +386,7 @@
* Luma (Y) samples should range from 64 to 940, inclusive
* Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
*/
- RANGE_LIMITED = 2 << 27,
+ ADATASPACE_RANGE_LIMITED = 2 << 27,
/**
* Extended range is used for scRGB. Intended for use with
@@ -395,7 +395,7 @@
* color outside the sRGB gamut.
* Used to blend / merge multiple dataspaces on a single display.
*/
- RANGE_EXTENDED = 3 << 27,
+ ADATASPACE_RANGE_EXTENDED = 3 << 27,
/**
* scRGB linear encoding
@@ -410,7 +410,8 @@
*
* Uses extended range, linear transfer and BT.709 standard.
*/
- ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED
+ ADATASPACE_SCRGB_LINEAR = 406913024, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR |
+ // ADATASPACE_RANGE_EXTENDED
/**
* sRGB gamma encoding
@@ -425,7 +426,8 @@
*
* Uses full range, sRGB transfer BT.709 standard.
*/
- ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL
+ ADATASPACE_SRGB = 142671872, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_FULL
/**
* scRGB
@@ -440,14 +442,16 @@
*
* Uses extended range, sRGB transfer and BT.709 standard.
*/
- ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED
+ ADATASPACE_SCRGB = 411107328, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_EXTENDED
/**
* Display P3
*
* Uses full range, sRGB transfer and D65 DCI-P3 standard.
*/
- ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL
+ ADATASPACE_DISPLAY_P3 = 143261696, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -456,7 +460,8 @@
*
* Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
+ ADATASPACE_BT2020_PQ = 163971072, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -465,7 +470,8 @@
*
* Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084
+ // | ADATASPACE_RANGE_LIMITED
/**
* Adobe RGB
@@ -475,7 +481,8 @@
* Note: Application is responsible for gamma encoding the data as
* a 2.2 gamma encoding is not supported in HW.
*/
- ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
+ ADATASPACE_ADOBE_RGB = 151715840, // ADATASPACE_STANDARD_ADOBE_RGB |
+ // ADATASPACE_TRANSFER_GAMMA2_2 | ADATASPACE_RANGE_FULL
/**
* JPEG File Interchange Format (JFIF)
@@ -484,7 +491,8 @@
*
* Uses full range, SMPTE 170M transfer and BT.601_625 standard.
*/
- ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
+ ADATASPACE_JFIF = 146931712, // ADATASPACE_STANDARD_BT601_625 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 601 (BT.601) - 625-line
@@ -493,7 +501,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.601_625 standard.
*/
- ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT601_625 = 281149440, // ADATASPACE_STANDARD_BT601_625 |
+ // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED
/**
* ITU-R Recommendation 601 (BT.601) - 525-line
@@ -502,7 +511,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
- ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT601_525 = 281280512, // ADATASPACE_STANDARD_BT601_525 |
+ // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -511,7 +521,8 @@
*
* Uses full range, SMPTE 170M transfer and BT2020 standard.
*/
- ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
+ ADATASPACE_BT2020 = 147193856, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 709 (BT.709)
@@ -520,7 +531,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.709 standard.
*/
- ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT709 = 281083904, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_LIMITED
/**
* SMPTE EG 432-1 and SMPTE RP 431-2
@@ -532,7 +544,8 @@
* Note: Application is responsible for gamma encoding the data as
* a 2.6 gamma encoding is not supported in HW.
*/
- ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
+ ADATASPACE_DCI_P3 = 155844608, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_GAMMA2_6 |
+ // ADATASPACE_RANGE_FULL
/**
* sRGB linear encoding
@@ -546,21 +559,24 @@
*
* Uses full range, linear transfer and BT.709 standard.
*/
- ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
+ ADATASPACE_SRGB_LINEAR = 138477568, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR |
+ // ADATASPACE_RANGE_FULL
/**
* Hybrid Log Gamma encoding
*
* Uses full range, hybrid log gamma transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
+ ADATASPACE_BT2020_HLG = 168165376, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
+ // ADATASPACE_RANGE_FULL
/**
* ITU Hybrid Log Gamma encoding
*
* Uses limited range, hybrid log gamma transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
+ // ADATASPACE_RANGE_LIMITED
/**
* Depth
@@ -574,7 +590,37 @@
*
* Embedded depth metadata following the dynamic depth specification.
*/
- ADATASPACE_DYNAMIC_DEPTH = 4098
+ ADATASPACE_DYNAMIC_DEPTH = 4098,
+
+#ifndef ADATASPACE_SKIP_LEGACY_DEFINES
+ STANDARD_MASK = ADATASPACE_STANDARD_MASK,
+ STANDARD_UNSPECIFIED = ADATASPACE_STANDARD_UNSPECIFIED,
+ STANDARD_BT709 = ADATASPACE_STANDARD_BT709,
+ STANDARD_BT601_625 = ADATASPACE_STANDARD_BT601_625,
+ STANDARD_BT601_625_UNADJUSTED = ADATASPACE_STANDARD_BT601_625_UNADJUSTED,
+ STANDARD_BT601_525 = ADATASPACE_STANDARD_BT601_525,
+ STANDARD_BT601_525_UNADJUSTED = ADATASPACE_STANDARD_BT601_525_UNADJUSTED,
+ STANDARD_BT470M = ADATASPACE_STANDARD_BT470M,
+ STANDARD_BT2020 = ADATASPACE_STANDARD_BT2020,
+ STANDARD_FILM = ADATASPACE_STANDARD_FILM,
+ STANDARD_DCI_P3 = ADATASPACE_STANDARD_DCI_P3,
+ STANDARD_ADOBE_RGB = ADATASPACE_STANDARD_ADOBE_RGB,
+ TRANSFER_MASK = ADATASPACE_TRANSFER_MASK,
+ TRANSFER_UNSPECIFIED = ADATASPACE_TRANSFER_UNSPECIFIED,
+ TRANSFER_LINEAR = ADATASPACE_TRANSFER_LINEAR,
+ TRANSFER_SMPTE_170M = ADATASPACE_TRANSFER_SMPTE_170M,
+ TRANSFER_GAMMA2_2 = ADATASPACE_TRANSFER_GAMMA2_2,
+ TRANSFER_GAMMA2_6 = ADATASPACE_TRANSFER_GAMMA2_6,
+ TRANSFER_GAMMA2_8 = ADATASPACE_TRANSFER_GAMMA2_8,
+ TRANSFER_SRGB = ADATASPACE_TRANSFER_SRGB,
+ TRANSFER_ST2084 = ADATASPACE_TRANSFER_ST2084,
+ TRANSFER_HLG = ADATASPACE_TRANSFER_HLG,
+ RANGE_MASK = ADATASPACE_RANGE_MASK,
+ RANGE_UNSPECIFIED = ADATASPACE_RANGE_UNSPECIFIED,
+ RANGE_FULL = ADATASPACE_RANGE_FULL,
+ RANGE_LIMITED = ADATASPACE_RANGE_LIMITED,
+ RANGE_EXTENDED = ADATASPACE_RANGE_EXTENDED,
+#endif
};
__END_DECLS
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 21798d0..e0e30c3 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -46,6 +46,9 @@
#define ANDROID_HARDWARE_BUFFER_H
#include <android/rect.h>
+#define ADATASPACE_SKIP_LEGACY_DEFINES
+#include <android/data_space.h>
+#undef ADATASPACE_SKIP_LEGACY_DEFINES
#include <inttypes.h>
#include <sys/cdefs.h>
diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h
index 0d5727d..68ac7e0 100644
--- a/libs/nativewindow/include/android/native_window_aidl.h
+++ b/libs/nativewindow/include/android/native_window_aidl.h
@@ -103,14 +103,22 @@
binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
reset();
- return ANativeWindow_readFromParcel(parcel, &mWindow);
+ if (__builtin_available(android __ANDROID_API_U__, *)) {
+ return ANativeWindow_readFromParcel(parcel, &mWindow);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
}
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
if (!mWindow) {
return STATUS_BAD_VALUE;
}
- return ANativeWindow_writeToParcel(mWindow, parcel);
+ if (__builtin_available(android __ANDROID_API_U__, *)) {
+ return ANativeWindow_writeToParcel(mWindow, parcel);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
}
/**
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 21931bb..4918601 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -21,6 +21,7 @@
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
+#include <errno.h>
__BEGIN_DECLS
@@ -105,6 +106,76 @@
AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16,
};
+/**
+ * Additional options for AHardwareBuffer_allocate2. These correspond to
+ * android.hardware.graphics.common.ExtendableType
+ */
+typedef struct {
+ const char* _Nonnull name;
+ int64_t value;
+} AHardwareBufferLongOptions;
+
+enum AHardwareBufferStatus : int32_t {
+ /* Success, no error */
+ AHARDWAREBUFFER_STATUS_OK = 0,
+ /* There's insufficient memory to satisfy the request */
+ AHARDWAREBUFFER_STATUS_NO_MEMORY = -ENOMEM,
+ /* The given argument is invalid */
+ AHARDWAREBUFFER_STATUS_BAD_VALUE = -EINVAL,
+ /* The requested operation is not supported by the device */
+ AHARDWAREBUFFER_STATUS_UNSUPPORTED = -ENOSYS,
+ /* An unknown error occurred */
+ AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR = (-2147483647 - 1),
+};
+
+/**
+ * Allocates a buffer that matches the passed AHardwareBuffer_Desc with additional options
+ *
+ * If allocation succeeds, the buffer can be used according to the
+ * usage flags specified in its description. If a buffer is used in ways
+ * not compatible with its usage flags, the results are undefined and
+ * may include program termination.
+ *
+ * @param desc The AHardwareBuffer_Desc that describes the allocation to request. Note that `stride`
+ * is ignored.
+ * @param additionalOptions A pointer to an array of AHardwareBufferLongOptions with additional
+ * string key + long value options that may be specified. May be null if
+ * `additionalOptionsSize` is 0
+ * @param additionalOptionsSize The number of additional options to pass
+ * @param outBuffer The resulting buffer allocation
+ * @return AHARDWAREBUFFER_STATUS_OK on success
+ * AHARDWAREBUFFER_STATUS_NO_MEMORY if there's insufficient resources for the allocation
+ * AHARDWAREBUFFER_STATUS_BAD_VALUE if the provided description & options are not supported
+ * by the device
+ * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error
+ * any reason. The returned buffer has a reference count of 1.
+ */
+enum AHardwareBufferStatus AHardwareBuffer_allocate2(
+ const AHardwareBuffer_Desc* _Nonnull desc,
+ const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Queries the dataspace of the given AHardwareBuffer.
+ *
+ * @param buffer The non-null buffer for which to query the Dataspace
+ * @return The dataspace of the buffer, or ADATASPACE_UNKNOWN if one hasn't been set
+ */
+enum ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets the dataspace of the given AHardwareBuffer
+ * @param buffer The non-null buffer for which to set the dataspace
+ * @param dataSpace The dataspace to set
+ * @return AHARDWAREBUFFER_STATUS_OK on success,
+ * AHARDWAREBUFFER_STATUS_UNSUPPORTED if the device doesn't support setting the dataspace,
+ * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other failure.
+ */
+enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* _Nonnull buffer,
+ enum ADataSpace dataSpace)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
__END_DECLS
#endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index dcb5068..a185a59 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,6 +2,7 @@
global:
AHardwareBuffer_acquire;
AHardwareBuffer_allocate;
+ AHardwareBuffer_allocate2; # llndk # systemapi
AHardwareBuffer_createFromHandle; # llndk # systemapi
AHardwareBuffer_describe;
AHardwareBuffer_getId; # introduced=31
@@ -16,6 +17,8 @@
AHardwareBuffer_unlock;
AHardwareBuffer_readFromParcel; # introduced=34
AHardwareBuffer_writeToParcel; # introduced=34
+ AHardwareBuffer_getDataSpace; # llndk # systemapi
+ AHardwareBuffer_setDataSpace; # llndk # systemapi
ANativeWindowBuffer_getHardwareBuffer; # llndk
ANativeWindow_OemStorageGet; # llndk
ANativeWindow_OemStorageSet; # llndk
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
index ef863b6..1f0128a 100644
--- a/libs/nativewindow/tests/AHardwareBufferTest.cpp
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "AHardwareBuffer_test"
//#define LOG_NDEBUG 0
+#include <android-base/properties.h>
+#include <android/data_space.h>
#include <android/hardware/graphics/common/1.0/types.h>
#include <gtest/gtest.h>
#include <private/android/AHardwareBufferHelpers.h>
@@ -26,6 +28,10 @@
using namespace android;
using android::hardware::graphics::common::V1_0::BufferUsage;
+static bool IsCuttlefish() {
+ return ::android::base::GetProperty("ro.product.board", "") == "cutf";
+}
+
static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected,
uint64_t actual, const char* type) {
std::ostringstream ss;
@@ -170,3 +176,83 @@
EXPECT_NE(id1, id2);
}
+
+TEST(AHardwareBufferTest, Allocate2NoExtras) {
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, nullptr, 0, &buffer));
+ uint64_t id = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id));
+ EXPECT_NE(0, id);
+ AHardwareBuffer_Desc desc2{};
+ AHardwareBuffer_describe(buffer, &desc2);
+ EXPECT_EQ(desc.width, desc2.width);
+ EXPECT_EQ(desc.height, desc2.height);
+ EXPECT_GE(desc2.stride, desc2.width);
+
+ AHardwareBuffer_release(buffer);
+}
+
+TEST(AHardwareBufferTest, Allocate2WithExtras) {
+ if (!IsCuttlefish()) {
+ GTEST_SKIP() << "Unknown gralloc HAL, cannot test extras";
+ }
+
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 48,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ std::array<AHardwareBufferLongOptions, 1> extras = {{
+ {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3},
+ }};
+ ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, extras.data(), extras.size(), &buffer));
+ uint64_t id = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id));
+ EXPECT_NE(0, id);
+ AHardwareBuffer_Desc desc2{};
+ AHardwareBuffer_describe(buffer, &desc2);
+ EXPECT_EQ(desc.width, desc2.width);
+ EXPECT_EQ(desc.height, desc2.height);
+ EXPECT_GE(desc2.stride, desc2.width);
+
+ EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer));
+
+ AHardwareBuffer_release(buffer);
+}
+
+TEST(AHardwareBufferTest, GetSetDataspace) {
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 48,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ ASSERT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer));
+
+ EXPECT_EQ(ADATASPACE_UNKNOWN, AHardwareBuffer_getDataSpace(buffer));
+ AHardwareBufferStatus status = AHardwareBuffer_setDataSpace(buffer, ADATASPACE_DISPLAY_P3);
+ if (status != AHARDWAREBUFFER_STATUS_UNSUPPORTED) {
+ EXPECT_EQ(0, status);
+ EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer));
+ }
+
+ AHardwareBuffer_release(buffer);
+}
\ No newline at end of file
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index 30737c1..d7c7eb3 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -31,6 +31,7 @@
"device-tests",
],
shared_libs: [
+ "libbase",
"libgui",
"liblog",
"libnativewindow",
@@ -44,5 +45,8 @@
"ANativeWindowTest.cpp",
"c_compatibility.c",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 2053c6a..d688b51 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -236,7 +236,13 @@
err = selectEGLConfig(display, format, 0, &config);
if (err != NO_ERROR) {
// this EGL is too lame for android
- LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"
+ " (format: %d, vendor: %s, version: %s, extensions: %s, Client"
+ " API: %s)",
+ format, eglQueryString(display, EGL_VENDOR),
+ eglQueryString(display, EGL_VERSION),
+ eglQueryString(display, EGL_EXTENSIONS),
+ eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
}
}
}
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index e9b5dec..a5aca99 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -384,8 +384,8 @@
status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
@@ -400,6 +400,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index 474d381..152b35a 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -371,7 +371,7 @@
status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -383,6 +383,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 03ff58a..d6970e0 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -1069,7 +1069,7 @@
status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage,
@@ -1084,6 +1084,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
if (mAidlAllocator) {
AllocationResult result;
#pragma clang diagnostic push
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index 2ec6d18..b07e155 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -19,6 +19,7 @@
#include <ui/Gralloc5.h>
+#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
@@ -215,55 +216,75 @@
status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t *outStride,
- buffer_handle_t *outBufferHandles, bool importBuffers) const {
- auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage);
+ uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
+ auto result = allocate(GraphicBufferAllocator::AllocationRequest{
+ .importBuffer = importBuffers,
+ .width = width,
+ .height = height,
+ .format = format,
+ .layerCount = layerCount,
+ .usage = usage,
+ .requestorName = requestorName,
+ });
+
+ *outStride = result.stride;
+ outBufferHandles[0] = result.handle;
+ return result.status;
+}
+
+GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate(
+ const GraphicBufferAllocator::AllocationRequest& request) const {
+ auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height,
+ request.format, request.layerCount, request.usage);
if (!descriptorInfo) {
- return BAD_VALUE;
+ return GraphicBufferAllocator::AllocationResult{BAD_VALUE};
+ }
+
+ descriptorInfo->additionalOptions.reserve(request.extras.size());
+ for (const auto& option : request.extras) {
+ ExtendableType type;
+ type.name = option.name;
+ type.value = option.value;
+ descriptorInfo->additionalOptions.push_back(std::move(type));
}
AllocationResult result;
- auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result);
+ auto status = mAllocator->allocate2(*descriptorInfo, 1, &result);
if (!status.isOk()) {
auto error = status.getExceptionCode();
if (error == EX_SERVICE_SPECIFIC) {
- error = status.getServiceSpecificError();
+ switch (static_cast<AllocationError>(status.getServiceSpecificError())) {
+ case AllocationError::BAD_DESCRIPTOR:
+ error = BAD_VALUE;
+ break;
+ case AllocationError::NO_RESOURCES:
+ error = NO_MEMORY;
+ break;
+ default:
+ error = UNKNOWN_ERROR;
+ break;
+ }
}
- if (error == OK) {
- error = UNKNOWN_ERROR;
- }
- return error;
+ return GraphicBufferAllocator::AllocationResult{error};
}
- if (importBuffers) {
- for (uint32_t i = 0; i < bufferCount; i++) {
- auto handle = makeFromAidl(result.buffers[i]);
- auto error = mMapper.importBuffer(handle, &outBufferHandles[i]);
- native_handle_delete(handle);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
- }
- return error;
- }
+ GraphicBufferAllocator::AllocationResult ret{OK};
+ if (request.importBuffer) {
+ auto handle = makeFromAidl(result.buffers[0]);
+ auto error = mMapper.importBuffer(handle, &ret.handle);
+ native_handle_delete(handle);
+ if (error != NO_ERROR) {
+ return GraphicBufferAllocator::AllocationResult{error};
}
} else {
- for (uint32_t i = 0; i < bufferCount; i++) {
- outBufferHandles[i] = dupFromAidl(result.buffers[i]);
- if (!outBufferHandles[i]) {
- for (uint32_t j = 0; j < i; j++) {
- auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]);
- native_handle_close(buffer);
- native_handle_delete(buffer);
- outBufferHandles[j] = nullptr;
- }
- return NO_MEMORY;
- }
+ ret.handle = dupFromAidl(result.buffers[0]);
+ if (!ret.handle) {
+ return GraphicBufferAllocator::AllocationResult{NO_MEMORY};
}
}
- *outStride = result.stride;
+ ret.stride = result.stride;
// Release all the resources held by AllocationResult (specifically any remaining FDs)
result = {};
@@ -272,7 +293,7 @@
// is marked apex_available (b/214400477) and libbinder isn't (which of course is correct)
// IPCThreadState::self()->flushCommands();
- return OK;
+ return ret;
}
void Gralloc5Mapper::preload() {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 429760f..c007fdb 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -106,6 +106,26 @@
inUsage, inStride);
}
+GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& request)
+ : GraphicBuffer() {
+ GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
+ auto result = allocator.allocate(request);
+ mInitCheck = result.status;
+ if (result.status == NO_ERROR) {
+ handle = result.handle;
+ stride = result.stride;
+
+ mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
+
+ width = static_cast<int>(request.width);
+ height = static_cast<int>(request.height);
+ format = request.format;
+ layerCount = request.layerCount;
+ usage = request.usage;
+ usage_deprecated = int(usage);
+ }
+}
+
GraphicBuffer::~GraphicBuffer()
{
ATRACE_CALL();
@@ -143,6 +163,10 @@
const_cast<GraphicBuffer*>(this));
}
+status_t GraphicBuffer::getDataspace(ui::Dataspace* outDataspace) const {
+ return mBufferMapper.getDataspace(handle, outDataspace);
+}
+
status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage)
{
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index eb0bd4e..98082fb 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -113,6 +113,79 @@
ALOGD("%s", s.c_str());
}
+auto GraphicBufferAllocator::allocate(const AllocationRequest& request) -> AllocationResult {
+ ATRACE_CALL();
+ if (!request.width || !request.height) {
+ return AllocationResult(BAD_VALUE);
+ }
+
+ const auto width = request.width;
+ const auto height = request.height;
+
+ const uint32_t bpp = bytesPerPixel(request.format);
+ if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": Requesting too large a buffer size",
+ request.width, request.height, request.layerCount, request.format, request.usage);
+ return AllocationResult(BAD_VALUE);
+ }
+
+ if (request.layerCount < 1) {
+ return AllocationResult(BAD_VALUE);
+ }
+
+ auto result = mAllocator->allocate(request);
+ if (result.status == UNKNOWN_TRANSACTION) {
+ if (!request.extras.empty()) {
+ ALOGE("Failed to allocate with additional options, allocator version mis-match? "
+ "gralloc version = %d",
+ (int)mMapper.getMapperVersion());
+ return result;
+ }
+ // If there's no additional options, fall back to previous allocate version
+ result.status = mAllocator->allocate(request.requestorName, request.width, request.height,
+ request.format, request.layerCount, request.usage,
+ &result.stride, &result.handle, request.importBuffer);
+ }
+
+ if (result.status != NO_ERROR) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": %d",
+ request.width, request.height, request.layerCount, request.format, request.usage,
+ result.status);
+ return result;
+ }
+
+ if (!request.importBuffer) {
+ return result;
+ }
+ size_t bufSize;
+
+ // if stride has no meaning or is too large,
+ // approximate size with the input width instead
+ if ((result.stride) != 0 &&
+ std::numeric_limits<size_t>::max() / height / (result.stride) < static_cast<size_t>(bpp)) {
+ bufSize = static_cast<size_t>(width) * height * bpp;
+ } else {
+ bufSize = static_cast<size_t>((result.stride)) * height * bpp;
+ }
+
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ alloc_rec_t rec;
+ rec.width = width;
+ rec.height = height;
+ rec.stride = result.stride;
+ rec.format = request.format;
+ rec.layerCount = request.layerCount;
+ rec.usage = request.usage;
+ rec.size = bufSize;
+ rec.requestorName = request.requestorName;
+ list.add(result.handle, rec);
+
+ return result;
+}
+
status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride,
@@ -141,7 +214,7 @@
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
- 1, stride, handle, importBuffer);
+ stride, handle, importBuffer);
if (error != NO_ERROR) {
ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
"usage %" PRIx64 ": %d",
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 496ba57..e6015e0 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -23,6 +23,7 @@
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include "GraphicBufferAllocator.h"
#include <string>
@@ -218,9 +219,13 @@
*/
virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
bool importBuffers = true) const = 0;
+
+ virtual GraphicBufferAllocator::AllocationResult allocate(
+ const GraphicBufferAllocator::AllocationRequest&) const {
+ return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION);
+ }
};
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index a7b6f492..e50bb3a 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -81,9 +81,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 7367549..035684a 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -82,9 +82,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index df43be8..0f469c0 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -174,9 +174,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc4Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h
index 44b97d1..f9e8f5e 100644
--- a/libs/ui/include/ui/Gralloc5.h
+++ b/libs/ui/include/ui/Gralloc5.h
@@ -172,10 +172,12 @@
[[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t *outStride,
- buffer_handle_t *outBufferHandles,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
bool importBuffers) const override;
+ [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate(
+ const GraphicBufferAllocator::AllocationRequest&) const override;
+
private:
const Gralloc5Mapper &mMapper;
std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator;
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index f859848..652d8ba 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -26,6 +26,7 @@
#include <android/hardware_buffer.h>
#include <ui/ANativeObjectBase.h>
+#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
@@ -103,6 +104,8 @@
uint32_t inLayerCount, uint64_t inUsage,
std::string requestorName = "<Unknown>");
+ GraphicBuffer(const GraphicBufferAllocator::AllocationRequest&);
+
// Create a GraphicBuffer from an existing handle.
enum HandleWrapMethod : uint8_t {
// Wrap and use the handle directly. It assumes the handle has been
@@ -169,6 +172,8 @@
mGenerationNumber = generation;
}
+ status_t getDataspace(ui::Dataspace* outDataspace) const;
+
// This function is privileged. It requires access to the allocator
// device or service, which usually involves adding suitable selinux
// rules.
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 3ed988c..8f461e1 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -22,6 +22,7 @@
#include <memory>
#include <string>
+#include <vector>
#include <cutils/native_handle.h>
@@ -42,6 +43,35 @@
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
+ struct AdditionalOptions {
+ const char* name;
+ int64_t value;
+ };
+
+ struct AllocationRequest {
+ bool importBuffer;
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ uint32_t layerCount;
+ uint64_t usage;
+ std::string requestorName;
+ std::vector<AdditionalOptions> extras;
+ };
+
+ struct AllocationResult {
+ status_t status;
+ buffer_handle_t handle = nullptr;
+ uint32_t stride = 0;
+
+ explicit AllocationResult(status_t status) : status(status) {}
+
+ explicit AllocationResult(buffer_handle_t handle, uint32_t stride)
+ : status(OK), handle(handle), stride(stride) {}
+ };
+
+ AllocationResult allocate(const AllocationRequest&);
+
/**
* Allocates and imports a gralloc buffer.
*
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index f4c0afa..efca083 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
std::cout << "Setting expected stride to " << stride << std::endl;
EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
allocate)
- .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
+ .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
}
std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
};
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index d62e3e2..d02b387 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -35,7 +35,7 @@
MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
MOCK_METHOD(status_t, allocate,
(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint32_t layerCount, uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index bf6189d..654c903 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -38,6 +38,7 @@
BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH
+ BATTERY_PROP_PART_STATUS = 12, // equals BATTERY_PROPERTY_PART_STATUS
};
struct BatteryProperties {
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 45c9b5c..69f42bc 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -77,6 +77,7 @@
"InputCommonConverter.cpp",
"InputDeviceMetricsCollector.cpp",
"InputFilter.cpp",
+ "InputFilterCallbacks.cpp",
"InputProcessor.cpp",
"PointerChoreographer.cpp",
"PreferStylusOverTouchBlocker.cpp",
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 9c4a3eb..5d87d34 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -44,31 +44,9 @@
return event;
}
-NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
- return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId,
- static_cast<uint32_t>(event.source), event.displayId, event.policyFlags,
- static_cast<int32_t>(event.action), event.flags, event.keyCode,
- event.scanCode, event.metaState, event.downTime);
-}
-
-namespace {
-
-class RustCallbacks : public IInputFilter::BnInputFilterCallbacks {
-public:
- RustCallbacks(InputListenerInterface& nextListener) : mNextListener(nextListener) {}
- ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override {
- mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
- return ndk::ScopedAStatus::ok();
- }
-
-private:
- InputListenerInterface& mNextListener;
-};
-
-} // namespace
-
InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust)
- : mNextListener(listener), mCallbacks(ndk::SharedRefBase::make<RustCallbacks>(listener)) {
+ : mNextListener(listener),
+ mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener)) {
LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk());
LOG_ALWAYS_FATAL_IF(!mInputFilterRust);
}
@@ -92,11 +70,11 @@
}
void InputFilter::notifyKey(const NotifyKeyArgs& args) {
- if (!isFilterEnabled()) {
- mNextListener.notifyKey(args);
+ if (isFilterEnabled()) {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
return;
}
- LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
+ mNextListener.notifyKey(args);
}
void InputFilter::notifyMotion(const NotifyMotionArgs& args) {
@@ -138,6 +116,15 @@
}
}
+void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) {
+ std::scoped_lock _l(mLock);
+
+ if (mConfig.stickyKeysEnabled != enabled) {
+ mConfig.stickyKeysEnabled = enabled;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+ }
+}
+
void InputFilter::dump(std::string& dump) {
dump += "InputFilter:\n";
}
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
index 06f7d0e..9fa7a87 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -18,6 +18,7 @@
#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
#include <utils/Mutex.h>
+#include "InputFilterCallbacks.h"
#include "InputListener.h"
#include "NotifyArgs.h"
@@ -33,6 +34,7 @@
*/
virtual void dump(std::string& dump) = 0;
virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0;
+ virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0;
};
class InputFilter : public InputFilterInterface {
@@ -56,11 +58,12 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override;
+ void setAccessibilityStickyKeysEnabled(bool enabled) override;
void dump(std::string& dump) override;
private:
InputListenerInterface& mNextListener;
- std::shared_ptr<IInputFilterCallbacks> mCallbacks;
+ std::shared_ptr<InputFilterCallbacks> mCallbacks;
std::shared_ptr<IInputFilter> mInputFilterRust;
mutable std::mutex mLock;
InputFilterConfiguration mConfig GUARDED_BY(mLock);
diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp
new file mode 100644
index 0000000..8c8f5e8
--- /dev/null
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputFilterCallbacks"
+
+#include "InputFilterCallbacks.h"
+
+namespace android {
+
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
+ return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId,
+ static_cast<uint32_t>(event.source), event.displayId, event.policyFlags,
+ static_cast<int32_t>(event.action), event.flags, event.keyCode,
+ event.scanCode, event.metaState, event.downTime);
+}
+
+InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener)
+ : mNextListener(listener) {}
+
+ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) {
+ mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifierState,
+ int32_t lockedModifierState) {
+ std::scoped_lock _l(mLock);
+ mStickyModifierState.modifierState = modifierState;
+ mStickyModifierState.lockedModifierState = lockedModifierState;
+ ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d",
+ modifierState, lockedModifierState);
+ return ndk::ScopedAStatus::ok();
+}
+
+uint32_t InputFilterCallbacks::getModifierState() {
+ std::scoped_lock _l(mLock);
+ return mStickyModifierState.modifierState;
+}
+
+uint32_t InputFilterCallbacks::getLockedModifierState() {
+ std::scoped_lock _l(mLock);
+ return mStickyModifierState.lockedModifierState;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h
new file mode 100644
index 0000000..c0a80fb
--- /dev/null
+++ b/services/inputflinger/InputFilterCallbacks.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <android/binder_auto_utils.h>
+#include <utils/Mutex.h>
+#include <mutex>
+#include "InputListener.h"
+#include "NotifyArgs.h"
+
+/**
+ * The C++ component of InputFilter designed as a wrapper around the rust callback implementation.
+ */
+namespace android {
+
+using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks {
+public:
+ explicit InputFilterCallbacks(InputListenerInterface& listener);
+ ~InputFilterCallbacks() override = default;
+
+ uint32_t getModifierState();
+ uint32_t getLockedModifierState();
+
+private:
+ InputListenerInterface& mNextListener;
+ mutable std::mutex mLock;
+ struct StickyModifierState {
+ uint32_t modifierState;
+ uint32_t lockedModifierState;
+ } mStickyModifierState GUARDED_BY(mLock);
+
+ ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override;
+ ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState,
+ int32_t lockedModifierState) override;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 07a5041..0be4c32 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -327,14 +327,23 @@
std::set<DeviceId> touchDevicesToKeep;
std::set<DeviceId> stylusDevicesToKeep;
- // Mark the displayIds or deviceIds of PointerControllers currently needed.
+ // Mark the displayIds or deviceIds of PointerControllers currently needed, and create
+ // new PointerControllers if necessary.
for (const auto& info : mInputDeviceInfos) {
const uint32_t sources = info.getSources();
if (isFromSource(sources, AINPUT_SOURCE_MOUSE) ||
isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) {
- const int32_t resolvedDisplayId =
- getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
- mouseDisplaysToKeep.insert(resolvedDisplayId);
+ const int32_t displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
+ mouseDisplaysToKeep.insert(displayId);
+ // For mice, show the cursor immediately when the device is first connected or
+ // when it moves to a new display.
+ auto [mousePointerIt, isNewMousePointer] =
+ mMousePointersByDisplay.try_emplace(displayId,
+ getMouseControllerConstructor(displayId));
+ auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId());
+ if (isNewMouseDevice || isNewMousePointer) {
+ mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
}
if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
@@ -356,6 +365,11 @@
std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
});
+ std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
+ return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
+ [id](const auto& info) { return info.getId() == id; }) ==
+ mInputDeviceInfos.end();
+ });
// Notify the policy if there's a change on the pointer display ID.
notifyPointerDisplayIdChangedLocked();
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 9b809a1..f46419e 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -139,6 +139,7 @@
int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
+ std::set<DeviceId> mMouseDevices GUARDED_BY(mLock);
std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
bool mShowTouchesEnabled GUARDED_BY(mLock);
bool mStylusPointerIconEnabled GUARDED_BY(mLock);
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
index 14b41cd..2921d30 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -33,6 +33,9 @@
interface IInputFilterCallbacks {
/** Sends back a filtered key event */
void sendKeyEvent(in KeyEvent event);
+
+ /** Sends back modifier state */
+ void onModifierStateChanged(int modifierState, int lockedModifierState);
}
/** Returns if InputFilter is enabled */
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
index 3b2e88b..38b1612 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
@@ -22,4 +22,6 @@
parcelable InputFilterConfiguration {
// Threshold value for Bounce keys filter (check bounce_keys_filter.rs)
long bounceKeysThresholdNs;
+ // If sticky keys filter is enabled
+ boolean stickyKeysEnabled;
}
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 1a94f41..8d0ff4b 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -72,7 +72,8 @@
using android::os::InputEventInjectionSync;
namespace input_flags = com::android::input::flags;
-static const bool REMOVE_APP_SWITCH_DROPS = input_flags::remove_app_switch_drops();
+// TODO(b/312714754): remove the corresponding code, as well.
+static const bool REMOVE_APP_SWITCH_DROPS = true;
namespace android::inputdispatcher {
@@ -126,10 +127,6 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-bool isEmpty(const std::stringstream& ss) {
- return ss.rdbuf()->in_avail() == 0;
-}
-
inline const std::string binderToString(const sp<IBinder>& binder) {
if (binder == nullptr) {
return "<null>";
@@ -256,6 +253,14 @@
}
}
+std::bitset<MAX_POINTER_ID + 1> getPointerIds(const std::vector<PointerProperties>& pointers) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ for (const PointerProperties& pointer : pointers) {
+ pointerIds.set(pointer.id);
+ }
+ return pointerIds;
+}
+
std::string dumpRegion(const Region& region) {
if (region.isEmpty()) {
return "<empty>";
@@ -634,15 +639,15 @@
}
// We should consider all hovering pointers here. But for now, just use the first one
- const int32_t pointerId = entry.pointerProperties[0].id;
+ const PointerProperties& pointer = entry.pointerProperties[0];
std::set<sp<WindowInfoHandle>> oldWindows;
if (oldState != nullptr) {
- oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId);
+ oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointer.id);
}
std::set<sp<WindowInfoHandle>> newWindows =
- newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointerId);
+ newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointer.id);
// If the pointer is no longer in the new window set, send HOVER_EXIT.
for (const sp<WindowInfoHandle>& oldWindow : oldWindows) {
@@ -676,7 +681,7 @@
}
touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
}
- touchedWindow.addHoveringPointer(entry.deviceId, pointerId);
+ touchedWindow.addHoveringPointer(entry.deviceId, pointer);
if (canReceiveForegroundTouches(*newWindow->getInfo())) {
touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -744,6 +749,20 @@
return true;
}
+/**
+ * Return true if stylus is currently down anywhere on the specified display, and false otherwise.
+ */
+bool isStylusActiveInDisplay(
+ int32_t displayId,
+ const std::unordered_map<int32_t /*displayId*/, TouchState>& touchStatesByDisplay) {
+ const auto it = touchStatesByDisplay.find(displayId);
+ if (it == touchStatesByDisplay.end()) {
+ return false;
+ }
+ const TouchState& state = it->second;
+ return state.hasActiveStylus();
+}
+
} // namespace
// --- InputDispatcher ---
@@ -2139,7 +2158,6 @@
sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(
nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
InputEventInjectionResult& outInjectionResult) {
- std::string reason;
outInjectionResult = InputEventInjectionResult::FAILED; // Default result
int32_t displayId = getTargetDisplayId(entry);
@@ -2324,7 +2342,7 @@
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
const auto [x, y] = resolveTouchedPosition(entry);
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
- const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
+ const PointerProperties& pointer = entry.pointerProperties[pointerIndex];
// Outside targets should be added upon first dispatched DOWN event. That means, this should
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2332,7 +2350,7 @@
findTouchedWindowAtLocked(displayId, x, y, isStylus);
if (isDown) {
- targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointerId);
+ targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id);
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
@@ -2390,7 +2408,7 @@
if (isHoverAction) {
// The "windowHandle" is the target of this hovering pointer.
- tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
+ tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer);
}
// Set target flags.
@@ -2413,12 +2431,10 @@
// Update the temporary touch state.
if (!isHoverAction) {
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- pointerIds.set(pointerId);
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
tempTouchState.addOrUpdateWindow(windowHandle, InputTarget::DispatchMode::AS_IS,
- targetFlags, entry.deviceId, pointerIds,
+ targetFlags, entry.deviceId, {pointer},
isDownOrPointerDown
? std::make_optional(entry.eventTime)
: std::nullopt);
@@ -2441,7 +2457,7 @@
}
tempTouchState.addOrUpdateWindow(wallpaper,
InputTarget::DispatchMode::AS_IS,
- wallpaperFlags, entry.deviceId, pointerIds,
+ wallpaperFlags, entry.deviceId, {pointer},
entry.eventTime);
}
}
@@ -2452,12 +2468,12 @@
// make it pilfering. This will prevent other non-spy windows from getting this pointer,
// which is a specific behaviour that we want.
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) &&
+ if (touchedWindow.hasTouchingPointer(entry.deviceId, pointer.id) &&
touchedWindow.hasPilferingPointers(entry.deviceId)) {
// This window is already pilfering some pointers, and this new pointer is also
// going to it. Therefore, take over this pointer and don't give it to anyone
// else.
- touchedWindow.addPilferingPointer(entry.deviceId, pointerId);
+ touchedWindow.addPilferingPointer(entry.deviceId, pointer.id);
}
}
@@ -2526,8 +2542,8 @@
// Make a slippery exit from the old window.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
- const int32_t pointerId = entry.pointerProperties[0].id;
- pointerIds.set(pointerId);
+ const PointerProperties& pointer = entry.pointerProperties[0];
+ pointerIds.set(pointer.id);
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
@@ -2557,13 +2573,13 @@
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
InputTarget::DispatchMode::SLIPPERY_ENTER,
- targetFlags, entry.deviceId, pointerIds,
+ targetFlags, entry.deviceId, {pointer},
entry.eventTime);
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, entry.deviceId, pointerId, targets);
- tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointerId,
+ tempTouchState, entry.deviceId, pointer, targets);
+ tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
}
@@ -2572,14 +2588,12 @@
if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
// If no split, we suppose all touched windows should receive pointer down.
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
- for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ std::vector<PointerProperties> touchingPointers{entry.pointerProperties[pointerIndex]};
+ for (TouchedWindow& touchedWindow : tempTouchState.windows) {
// Ignore drag window for it should just track one pointer.
if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
continue;
}
- std::bitset<MAX_POINTER_ID + 1> touchingPointers;
- touchingPointers.set(entry.pointerProperties[pointerIndex].id);
touchedWindow.addTouchingPointers(entry.deviceId, touchingPointers);
}
}
@@ -2650,13 +2664,13 @@
// Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- std::bitset<MAX_POINTER_ID + 1> touchingPointers =
+ std::vector<PointerProperties> touchingPointers =
touchedWindow.getTouchingPointers(entry.deviceId);
- if (touchingPointers.none()) {
+ if (touchingPointers.empty()) {
continue;
}
addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
- touchedWindow.targetFlags, touchingPointers,
+ touchedWindow.targetFlags, getPointerIds(touchingPointers),
touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
}
@@ -5051,6 +5065,13 @@
return false;
}
+ // Ignore touches if stylus is down anywhere on screen
+ if (info.inputConfig.test(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH) &&
+ isStylusActiveInDisplay(info.displayId, mTouchStatesByDisplay)) {
+ LOG(INFO) << "Dropping touch from " << window->getName() << " because stylus is active";
+ return false;
+ }
+
return true;
}
@@ -5129,7 +5150,7 @@
for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
windowList += iwh->getName() + " ";
}
- ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
+ LOG(INFO) << "setInputWindows displayId=" << displayId << " " << windowList;
}
// Check preconditions for new input windows
@@ -5485,7 +5506,7 @@
// Erase old window.
ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->getTouchingPointers(deviceId);
+ std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId);
sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
state->removeWindowByToken(fromToken);
@@ -5497,17 +5518,17 @@
newTargetFlags |= InputTarget::Flags::FOREGROUND;
}
state->addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags,
- deviceId, pointerIds, downTimeInTarget);
+ deviceId, pointers, downTimeInTarget);
// Store the dragging window.
if (isDragDrop) {
- if (pointerIds.count() != 1) {
+ if (pointers.size() != 1) {
ALOGW("The drag and drop cannot be started when there is no pointer or more than 1"
" pointer on the window.");
return false;
}
// Track the pointer id for drag window and generate the drag state.
- const size_t id = firstMarkedBit(pointerIds);
+ const size_t id = pointers.begin()->id;
mDragState = std::make_unique<DragState>(toWindowHandle, id);
}
@@ -5524,7 +5545,7 @@
// Check if the wallpaper window should deliver the corresponding event.
transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
- *state, deviceId, pointerIds);
+ *state, deviceId, pointers);
}
} // release lock
@@ -5687,33 +5708,8 @@
if (!windowHandles.empty()) {
dump += INDENT2 "Windows:\n";
for (size_t i = 0; i < windowHandles.size(); i++) {
- const sp<WindowInfoHandle>& windowHandle = windowHandles[i];
- const WindowInfo* windowInfo = windowHandle->getInfo();
-
- dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
- "inputConfig=%s, alpha=%.2f, "
- "frame=[%d,%d][%d,%d], globalScale=%f, "
- "applicationInfo.name=%s, "
- "applicationInfo.token=%s, "
- "touchableRegion=",
- i, windowInfo->name.c_str(), windowInfo->id,
- windowInfo->displayId,
- windowInfo->inputConfig.string().c_str(),
- windowInfo->alpha, windowInfo->frame.left,
- windowInfo->frame.top, windowInfo->frame.right,
- windowInfo->frame.bottom, windowInfo->globalScaleFactor,
- windowInfo->applicationInfo.name.c_str(),
- binderToString(windowInfo->applicationInfo.token).c_str());
- dump += dumpRegion(windowInfo->touchableRegion);
- dump += StringPrintf(", ownerPid=%s, ownerUid=%s, dispatchingTimeout=%" PRId64
- "ms, hasToken=%s, "
- "touchOcclusionMode=%s\n",
- windowInfo->ownerPid.toString().c_str(),
- windowInfo->ownerUid.toString().c_str(),
- millis(windowInfo->dispatchingTimeout),
- binderToString(windowInfo->token).c_str(),
- toString(windowInfo->touchOcclusionMode).c_str());
- windowInfo->transform.dump(dump, "transform", INDENT4);
+ dump += StringPrintf(INDENT3 "%zu: %s", i,
+ streamableToString(*windowHandles[i]).c_str());
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -5802,11 +5798,10 @@
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
}
- std::stringstream inputStateDump;
- inputStateDump << connection->inputState;
- if (!isEmpty(inputStateDump)) {
+ std::string inputStateDump = streamableToString(connection->inputState);
+ if (!inputStateDump.empty()) {
dump += INDENT3 "InputState: ";
- dump += inputStateDump.str() + "\n";
+ dump += inputStateDump + "\n";
}
}
} else {
@@ -6027,8 +6022,10 @@
"input channel stole pointer stream");
options.deviceId = deviceId;
options.displayId = displayId;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
+ std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId);
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = getPointerIds(pointers);
options.pointerIds = pointerIds;
+
std::string canceledWindows;
for (const TouchedWindow& w : state.windows) {
const std::shared_ptr<InputChannel> channel =
@@ -6824,10 +6821,10 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t deviceId, int32_t pointerId,
+ TouchState& state, int32_t deviceId,
+ const PointerProperties& pointerProperties,
std::vector<InputTarget>& targets) const {
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- pointerIds.set(pointerId);
+ std::vector<PointerProperties> pointers{pointerProperties};
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
const bool newHasWallpaper = targetFlags.test(InputTarget::Flags::FOREGROUND) &&
@@ -6844,16 +6841,16 @@
if (oldWallpaper != nullptr) {
const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
addPointerWindowTargetLocked(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT,
- oldTouchedWindow.targetFlags, pointerIds,
+ oldTouchedWindow.targetFlags, getPointerIds(pointers),
oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
- state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper);
+ state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper);
}
if (newWallpaper != nullptr) {
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointerIds);
+ deviceId, pointers);
}
}
@@ -6862,7 +6859,7 @@
const sp<WindowInfoHandle> fromWindowHandle,
const sp<WindowInfoHandle> toWindowHandle,
TouchState& state, int32_t deviceId,
- std::bitset<MAX_POINTER_ID + 1> pointerIds) {
+ const std::vector<PointerProperties>& pointers) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -6891,7 +6888,7 @@
wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
- deviceId, pointerIds, downTimeInTarget);
+ deviceId, pointers, downTimeInTarget);
std::shared_ptr<Connection> wallpaperConnection =
getConnectionLocked(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 3f99b2d..010dbb2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -690,14 +690,15 @@
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t deviceId, int32_t pointerId,
+ TouchState& state, int32_t deviceId,
+ const PointerProperties& pointerProperties,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
const sp<android::gui::WindowInfoHandle> toWindowHandle,
TouchState& state, int32_t deviceId,
- std::bitset<MAX_POINTER_ID + 1> pointerIds) REQUIRES(mLock);
+ const std::vector<PointerProperties>& pointers) REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index e8d8c18..f8aa625 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -73,9 +73,9 @@
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
- std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
+ const std::vector<PointerProperties>& touchingPointers,
std::optional<nsecs_t> firstDownTimeInTarget) {
- if (touchingPointerIds.none()) {
+ if (touchingPointers.empty()) {
LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
return;
}
@@ -91,7 +91,7 @@
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when a pointer is down for the
// window.
- touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
+ touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
@@ -102,7 +102,7 @@
touchedWindow.windowHandle = windowHandle;
touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags = targetFlags;
- touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
+ touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
@@ -110,17 +110,17 @@
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
- DeviceId deviceId, int32_t hoveringPointerId) {
+ DeviceId deviceId, const PointerProperties& pointer) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, pointer);
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
- touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, pointer);
windows.push_back(touchedWindow);
}
@@ -234,6 +234,11 @@
});
}
+bool TouchState::hasActiveStylus() const {
+ return std::any_of(windows.begin(), windows.end(),
+ [](const TouchedWindow& window) { return window.hasActiveStylus(); });
+}
+
std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
int32_t pointerId) const {
std::set<sp<WindowInfoHandle>> out;
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index e0a84e8..3d534bc 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -46,11 +46,11 @@
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
- std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
+ const std::vector<PointerProperties>& touchingPointers,
std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- DeviceId deviceId, int32_t hoveringPointerId);
- void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId);
+ DeviceId deviceId, const PointerProperties& pointer);
+ void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
void clearHoveringPointers(DeviceId deviceId);
void removeAllPointersForDevice(DeviceId deviceId);
@@ -73,6 +73,8 @@
bool isDown(DeviceId deviceId) const;
bool hasHoveringPointers(DeviceId deviceId) const;
+ bool hasActiveStylus() const;
+
std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
DeviceId deviceId, int32_t pointerId) const;
std::string dump() const;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index cd0500c..037d7c8 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -26,9 +26,20 @@
namespace inputdispatcher {
+namespace {
+
+bool hasPointerId(const std::vector<PointerProperties>& pointers, int32_t pointerId) {
+ return std::find_if(pointers.begin(), pointers.end(),
+ [&pointerId](const PointerProperties& properties) {
+ return properties.id == pointerId;
+ }) != pointers.end();
+}
+
+} // namespace
+
bool TouchedWindow::hasHoveringPointers() const {
for (const auto& [_, state] : mDeviceStates) {
- if (state.hoveringPointerIds.any()) {
+ if (!state.hoveringPointers.empty()) {
return true;
}
}
@@ -42,7 +53,7 @@
}
const DeviceState& state = stateIt->second;
- return state.hoveringPointerIds.any();
+ return !state.hoveringPointers.empty();
}
void TouchedWindow::clearHoveringPointers(DeviceId deviceId) {
@@ -51,7 +62,7 @@
return;
}
DeviceState& state = stateIt->second;
- state.hoveringPointerIds.reset();
+ state.hoveringPointers.clear();
if (!state.hasPointers()) {
mDeviceStates.erase(stateIt);
}
@@ -63,22 +74,40 @@
return false;
}
const DeviceState& state = stateIt->second;
-
- return state.hoveringPointerIds.test(pointerId);
+ return hasPointerId(state.hoveringPointers, pointerId);
}
-void TouchedWindow::addHoveringPointer(DeviceId deviceId, int32_t pointerId) {
- mDeviceStates[deviceId].hoveringPointerIds.set(pointerId);
+void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) {
+ std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
+ const size_t initialSize = hoveringPointers.size();
+ std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) {
+ return properties.id == pointer.id;
+ });
+ if (hoveringPointers.size() != initialSize) {
+ LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this;
+ }
+ hoveringPointers.push_back(pointer);
}
void TouchedWindow::addTouchingPointers(DeviceId deviceId,
- std::bitset<MAX_POINTER_ID + 1> pointers) {
- mDeviceStates[deviceId].touchingPointerIds |= pointers;
+ const std::vector<PointerProperties>& pointers) {
+ std::vector<PointerProperties>& touchingPointers = mDeviceStates[deviceId].touchingPointers;
+ const size_t initialSize = touchingPointers.size();
+ for (const PointerProperties& pointer : pointers) {
+ std::erase_if(touchingPointers, [&pointer](const PointerProperties& properties) {
+ return properties.id == pointer.id;
+ });
+ }
+ if (touchingPointers.size() != initialSize) {
+ LOG(ERROR) << __func__ << ": " << dumpVector(pointers, streamableToString) << ", device "
+ << deviceId << " already in " << *this;
+ }
+ touchingPointers.insert(touchingPointers.end(), pointers.begin(), pointers.end());
}
bool TouchedWindow::hasTouchingPointers() const {
for (const auto& [_, state] : mDeviceStates) {
- if (state.touchingPointerIds.any()) {
+ if (!state.touchingPointers.empty()) {
return true;
}
}
@@ -86,21 +115,25 @@
}
bool TouchedWindow::hasTouchingPointers(DeviceId deviceId) const {
- return getTouchingPointers(deviceId).any();
+ return !getTouchingPointers(deviceId).empty();
}
bool TouchedWindow::hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const {
- return getTouchingPointers(deviceId).test(pointerId);
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return false;
+ }
+ const DeviceState& state = stateIt->second;
+ return hasPointerId(state.touchingPointers, pointerId);
}
-std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(DeviceId deviceId) const {
+std::vector<PointerProperties> TouchedWindow::getTouchingPointers(DeviceId deviceId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return {};
}
const DeviceState& state = stateIt->second;
-
- return state.touchingPointerIds;
+ return state.touchingPointers;
}
void TouchedWindow::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
@@ -118,7 +151,10 @@
}
DeviceState& state = stateIt->second;
- state.touchingPointerIds &= ~pointers;
+ std::erase_if(state.touchingPointers, [&pointers](const PointerProperties& properties) {
+ return pointers.test(properties.id);
+ });
+
state.pilferingPointerIds &= ~pointers;
if (!state.hasPointers()) {
@@ -126,10 +162,26 @@
}
}
+bool TouchedWindow::hasActiveStylus() const {
+ for (const auto& [_, state] : mDeviceStates) {
+ for (const PointerProperties& properties : state.touchingPointers) {
+ if (properties.toolType == ToolType::STYLUS) {
+ return true;
+ }
+ }
+ for (const PointerProperties& properties : state.hoveringPointers) {
+ if (properties.toolType == ToolType::STYLUS) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const {
std::set<DeviceId> deviceIds;
for (const auto& [deviceId, deviceState] : mDeviceStates) {
- if (deviceState.touchingPointerIds.any()) {
+ if (!deviceState.touchingPointers.empty()) {
deviceIds.insert(deviceId);
}
}
@@ -198,7 +250,7 @@
}
DeviceState& state = stateIt->second;
- state.touchingPointerIds.reset();
+ state.touchingPointers.clear();
state.pilferingPointerIds.reset();
state.downTimeInTarget.reset();
@@ -214,7 +266,9 @@
}
DeviceState& state = stateIt->second;
- state.hoveringPointerIds.set(pointerId, false);
+ std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) {
+ return properties.id == pointerId;
+ });
if (!state.hasPointers()) {
mDeviceStates.erase(stateIt);
@@ -228,7 +282,7 @@
}
DeviceState& state = stateIt->second;
- state.hoveringPointerIds.reset();
+ state.hoveringPointers.clear();
if (!state.hasPointers()) {
mDeviceStates.erase(stateIt);
@@ -236,11 +290,11 @@
}
std::string TouchedWindow::deviceStateToString(const TouchedWindow::DeviceState& state) {
- return StringPrintf("[touchingPointerIds=%s, "
- "downTimeInTarget=%s, hoveringPointerIds=%s, pilferingPointerIds=%s]",
- bitsetToString(state.touchingPointerIds).c_str(),
+ return StringPrintf("[touchingPointers=%s, "
+ "downTimeInTarget=%s, hoveringPointers=%s, pilferingPointerIds=%s]",
+ dumpVector(state.touchingPointers, streamableToString).c_str(),
toString(state.downTimeInTarget).c_str(),
- bitsetToString(state.hoveringPointerIds).c_str(),
+ dumpVector(state.hoveringPointers, streamableToString).c_str(),
bitsetToString(state.pilferingPointerIds).c_str());
}
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index c604353..0d1531f 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -38,17 +38,18 @@
bool hasHoveringPointers() const;
bool hasHoveringPointers(DeviceId deviceId) const;
bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const;
- void addHoveringPointer(DeviceId deviceId, int32_t pointerId);
+ void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
// Touching
bool hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const;
bool hasTouchingPointers() const;
bool hasTouchingPointers(DeviceId deviceId) const;
- std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(DeviceId deviceId) const;
- void addTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ std::vector<PointerProperties> getTouchingPointers(DeviceId deviceId) const;
+ void addTouchingPointers(DeviceId deviceId, const std::vector<PointerProperties>& pointers);
void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ bool hasActiveStylus() const;
std::set<DeviceId> getTouchingDeviceIds() const;
// Pilfering pointers
@@ -69,16 +70,16 @@
private:
struct DeviceState {
- std::bitset<MAX_POINTER_ID + 1> touchingPointerIds;
+ std::vector<PointerProperties> touchingPointers;
// The pointer ids of the pointers that this window is currently pilfering, by device
std::bitset<MAX_POINTER_ID + 1> pilferingPointerIds;
// Time at which the first action down occurred on this window, for each device
// NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE
// scenario.
std::optional<nsecs_t> downTimeInTarget;
- std::bitset<MAX_POINTER_ID + 1> hoveringPointerIds;
+ std::vector<PointerProperties> hoveringPointers;
- bool hasPointers() const { return touchingPointerIds.any() || hoveringPointerIds.any(); };
+ bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); };
};
std::map<DeviceId, DeviceState> mDeviceStates;
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index 340ff8e..e94a71f 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -27,6 +27,7 @@
};
use crate::bounce_keys_filter::BounceKeysFilter;
+use crate::sticky_keys_filter::StickyKeysFilter;
use log::{error, info};
use std::sync::{Arc, Mutex, RwLock};
@@ -91,6 +92,14 @@
let mut state = self.state.lock().unwrap();
let mut first_filter: Box<dyn Filter + Send + Sync> =
Box::new(BaseFilter::new(self.callbacks.clone()));
+ if config.stickyKeysEnabled {
+ first_filter = Box::new(StickyKeysFilter::new(
+ first_filter,
+ ModifierStateListener::new(self.callbacks.clone()),
+ ));
+ state.enabled = true;
+ info!("Sticky keys filter is installed");
+ }
if config.bounceKeysThresholdNs > 0 {
first_filter =
Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
@@ -125,34 +134,43 @@
}
}
+pub struct ModifierStateListener {
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+}
+
+impl ModifierStateListener {
+ /// Create a new InputFilter instance.
+ pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener {
+ Self { callbacks }
+ }
+
+ pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) {
+ let _ = self
+ .callbacks
+ .read()
+ .unwrap()
+ .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32);
+ }
+}
+
#[cfg(test)]
mod tests {
- use crate::input_filter::{test_filter::TestFilter, Filter, InputFilter};
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, InputFilter,
+ };
use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
- use binder::{Interface, Strong};
+ use binder::Strong;
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, IInputFilter::IInputFilter,
- IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent,
KeyEventAction::KeyEventAction,
};
use std::sync::{Arc, RwLock};
- struct FakeCallbacks {}
-
- impl Interface for FakeCallbacks {}
-
- impl IInputFilterCallbacks for FakeCallbacks {
- fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> {
- Result::Ok(())
- }
- }
-
#[test]
fn test_not_enabled_with_default_filter() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let input_filter = InputFilter::new(fake_callbacks);
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
let result = input_filter.isEnabled();
assert!(result.is_ok());
assert!(!result.unwrap());
@@ -160,17 +178,21 @@
#[test]
fn test_notify_key_with_no_filters() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let input_filter = InputFilter::new(fake_callbacks);
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks.clone())));
let event = create_key_event();
assert!(input_filter.notifyKey(&event).is_ok());
+ assert_eq!(test_callbacks.last_event().unwrap(), event);
}
#[test]
fn test_notify_key_with_filter() {
let test_filter = TestFilter::new();
- let input_filter = create_input_filter(Box::new(test_filter.clone()));
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::create_input_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
+ );
let event = create_key_event();
assert!(input_filter.notifyKey(&event).is_ok());
assert_eq!(test_filter.last_event().unwrap(), event);
@@ -179,7 +201,11 @@
#[test]
fn test_notify_devices_changed() {
let test_filter = TestFilter::new();
- let input_filter = create_input_filter(Box::new(test_filter.clone()));
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::create_input_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
+ );
assert!(input_filter
.notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }])
.is_ok());
@@ -188,21 +214,30 @@
#[test]
fn test_notify_configuration_changed_enabled_bounce_keys() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let input_filter = InputFilter::new(fake_callbacks);
- let result = input_filter
- .notifyConfigurationChanged(&InputFilterConfiguration { bounceKeysThresholdNs: 100 });
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
+ bounceKeysThresholdNs: 100,
+ stickyKeysEnabled: false,
+ });
assert!(result.is_ok());
let result = input_filter.isEnabled();
assert!(result.is_ok());
assert!(result.unwrap());
}
- fn create_input_filter(filter: Box<dyn Filter + Send + Sync>) -> InputFilter {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- InputFilter::create_input_filter(filter, Arc::new(RwLock::new(fake_callbacks)))
+ #[test]
+ fn test_notify_configuration_changed_enabled_sticky_keys() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
+ bounceKeysThresholdNs: 0,
+ stickyKeysEnabled: true,
+ });
+ assert!(result.is_ok());
+ let result = input_filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(result.unwrap());
}
fn create_key_event() -> KeyEvent {
@@ -272,3 +307,69 @@
}
}
}
+
+#[cfg(test)]
+pub mod test_callbacks {
+ use binder::Interface;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent,
+ };
+ use std::sync::{Arc, RwLock, RwLockWriteGuard};
+
+ #[derive(Default)]
+ struct TestCallbacksInner {
+ last_modifier_state: u32,
+ last_locked_modifier_state: u32,
+ last_event: Option<KeyEvent>,
+ }
+
+ #[derive(Default, Clone)]
+ pub struct TestCallbacks(Arc<RwLock<TestCallbacksInner>>);
+
+ impl Interface for TestCallbacks {}
+
+ impl TestCallbacks {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ fn inner(&self) -> RwLockWriteGuard<'_, TestCallbacksInner> {
+ self.0.write().unwrap()
+ }
+
+ pub fn last_event(&self) -> Option<KeyEvent> {
+ self.0.read().unwrap().last_event
+ }
+
+ pub fn clear(&mut self) {
+ self.inner().last_event = None;
+ self.inner().last_modifier_state = 0;
+ self.inner().last_locked_modifier_state = 0;
+ }
+
+ pub fn get_last_modifier_state(&self) -> u32 {
+ self.0.read().unwrap().last_modifier_state
+ }
+
+ pub fn get_last_locked_modifier_state(&self) -> u32 {
+ self.0.read().unwrap().last_locked_modifier_state
+ }
+ }
+
+ impl IInputFilterCallbacks for TestCallbacks {
+ fn sendKeyEvent(&self, event: &KeyEvent) -> binder::Result<()> {
+ self.inner().last_event = Some(*event);
+ Result::Ok(())
+ }
+
+ fn onModifierStateChanged(
+ &self,
+ modifier_state: i32,
+ locked_modifier_state: i32,
+ ) -> std::result::Result<(), binder::Status> {
+ self.inner().last_modifier_state = modifier_state as u32;
+ self.inner().last_locked_modifier_state = locked_modifier_state as u32;
+ Result::Ok(())
+ }
+ }
+}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index 68cd480..fa16898 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -21,6 +21,7 @@
mod bounce_keys_filter;
mod input_filter;
+mod sticky_keys_filter;
use crate::input_filter::InputFilter;
use binder::{
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
new file mode 100644
index 0000000..da581b8
--- /dev/null
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Sticky keys input filter implementation.
+//! Sticky keys is an accessibility feature that assists users who have physical disabilities or
+//! helps users reduce repetitive strain injury. It serializes keystrokes instead of pressing
+//! multiple keys at a time, allowing the user to press and release a modifier key, such as Shift,
+//! Ctrl, Alt, or any other modifier key, and have it remain active until any other key is pressed.
+use crate::input_filter::{Filter, ModifierStateListener};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+};
+use std::collections::HashSet;
+
+// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h
+const KEYCODE_ALT_LEFT: i32 = 57;
+const KEYCODE_ALT_RIGHT: i32 = 58;
+const KEYCODE_SHIFT_LEFT: i32 = 59;
+const KEYCODE_SHIFT_RIGHT: i32 = 60;
+const KEYCODE_SYM: i32 = 63;
+const KEYCODE_CTRL_LEFT: i32 = 113;
+const KEYCODE_CTRL_RIGHT: i32 = 114;
+const KEYCODE_CAPS_LOCK: i32 = 115;
+const KEYCODE_SCROLL_LOCK: i32 = 116;
+const KEYCODE_META_LEFT: i32 = 117;
+const KEYCODE_META_RIGHT: i32 = 118;
+const KEYCODE_FUNCTION: i32 = 119;
+const KEYCODE_NUM_LOCK: i32 = 143;
+
+// Modifier states: values are from /frameworks/native/include/android/input.h
+const META_ALT_ON: u32 = 0x02;
+const META_ALT_LEFT_ON: u32 = 0x10;
+const META_ALT_RIGHT_ON: u32 = 0x20;
+const META_SHIFT_ON: u32 = 0x01;
+const META_SHIFT_LEFT_ON: u32 = 0x40;
+const META_SHIFT_RIGHT_ON: u32 = 0x80;
+const META_CTRL_ON: u32 = 0x1000;
+const META_CTRL_LEFT_ON: u32 = 0x2000;
+const META_CTRL_RIGHT_ON: u32 = 0x4000;
+const META_META_ON: u32 = 0x10000;
+const META_META_LEFT_ON: u32 = 0x20000;
+const META_META_RIGHT_ON: u32 = 0x40000;
+
+pub struct StickyKeysFilter {
+ next: Box<dyn Filter + Send + Sync>,
+ listener: ModifierStateListener,
+ /// Tracking devices that contributed to the modifier state.
+ contributing_devices: HashSet<i32>,
+ /// State describing the current enabled modifiers. This contain both locked and non-locked
+ /// modifier state bits.
+ modifier_state: u32,
+ /// State describing the current locked modifiers. These modifiers will not be cleared on a
+ /// non-modifier key press. They will be cleared only if the locked modifier key is pressed
+ /// again.
+ locked_modifier_state: u32,
+}
+
+impl StickyKeysFilter {
+ /// Create a new StickyKeysFilter instance.
+ pub fn new(
+ next: Box<dyn Filter + Send + Sync>,
+ listener: ModifierStateListener,
+ ) -> StickyKeysFilter {
+ Self {
+ next,
+ listener,
+ contributing_devices: HashSet::new(),
+ modifier_state: 0,
+ locked_modifier_state: 0,
+ }
+ }
+}
+
+impl Filter for StickyKeysFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ let up = event.action == KeyEventAction::UP;
+ let mut modifier_state = self.modifier_state;
+ let mut locked_modifier_state = self.locked_modifier_state;
+ if !is_ephemeral_modifier_key(event.keyCode) {
+ // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
+ // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
+ // the KeyEvent.
+ let old_modifier_state = event.metaState as u32;
+ let mut new_event = *event;
+ // Send the current modifier state with the key event before clearing non-locked
+ // modifier state
+ new_event.metaState =
+ (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32;
+ self.next.notify_key(&new_event);
+ if up && !is_modifier_key(event.keyCode) {
+ modifier_state =
+ clear_ephemeral_modifier_state(modifier_state) | locked_modifier_state;
+ }
+ } else if up {
+ // Update contributing devices to track keyboards
+ self.contributing_devices.insert(event.deviceId);
+ // If ephemeral modifier key, capture the key and update the sticky modifier states
+ let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode);
+ let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode);
+ if locked_modifier_state & modifier_key_mask != 0 {
+ locked_modifier_state &= !symmetrical_modifier_key_mask;
+ modifier_state &= !symmetrical_modifier_key_mask;
+ } else if modifier_key_mask & modifier_state != 0 {
+ locked_modifier_state |= modifier_key_mask;
+ modifier_state =
+ (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask;
+ } else {
+ modifier_state |= modifier_key_mask;
+ }
+ }
+ if self.modifier_state != modifier_state
+ || self.locked_modifier_state != locked_modifier_state
+ {
+ self.modifier_state = modifier_state;
+ self.locked_modifier_state = locked_modifier_state;
+ self.listener.modifier_state_changed(modifier_state, locked_modifier_state);
+ }
+ }
+
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
+ // Clear state if all contributing devices removed
+ self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
+ if self.contributing_devices.is_empty()
+ && (self.modifier_state != 0 || self.locked_modifier_state != 0)
+ {
+ self.modifier_state = 0;
+ self.locked_modifier_state = 0;
+ self.listener.modifier_state_changed(0, 0);
+ }
+ self.next.notify_devices_changed(device_infos);
+ }
+}
+
+fn is_modifier_key(keycode: i32) -> bool {
+ matches!(
+ keycode,
+ KEYCODE_ALT_LEFT
+ | KEYCODE_ALT_RIGHT
+ | KEYCODE_SHIFT_LEFT
+ | KEYCODE_SHIFT_RIGHT
+ | KEYCODE_CTRL_LEFT
+ | KEYCODE_CTRL_RIGHT
+ | KEYCODE_META_LEFT
+ | KEYCODE_META_RIGHT
+ | KEYCODE_SYM
+ | KEYCODE_FUNCTION
+ | KEYCODE_CAPS_LOCK
+ | KEYCODE_NUM_LOCK
+ | KEYCODE_SCROLL_LOCK
+ )
+}
+
+fn is_ephemeral_modifier_key(keycode: i32) -> bool {
+ matches!(
+ keycode,
+ KEYCODE_ALT_LEFT
+ | KEYCODE_ALT_RIGHT
+ | KEYCODE_SHIFT_LEFT
+ | KEYCODE_SHIFT_RIGHT
+ | KEYCODE_CTRL_LEFT
+ | KEYCODE_CTRL_RIGHT
+ | KEYCODE_META_LEFT
+ | KEYCODE_META_RIGHT
+ )
+}
+
+fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 {
+ match keycode {
+ KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON,
+ KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON,
+ KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON,
+ KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON,
+ KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON,
+ KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON,
+ KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON,
+ KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON,
+ _ => 0,
+ }
+}
+
+/// Modifier mask including both left and right versions of a modifier key.
+fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 {
+ match keycode {
+ KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON,
+ KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => {
+ META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON
+ }
+ KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => {
+ META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON
+ }
+ KEYCODE_META_LEFT | KEYCODE_META_RIGHT => {
+ META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON
+ }
+ _ => 0,
+ }
+}
+
+fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 {
+ modifier_state
+ & !(META_ALT_LEFT_ON
+ | META_ALT_RIGHT_ON
+ | META_ALT_ON
+ | META_SHIFT_LEFT_ON
+ | META_SHIFT_RIGHT_ON
+ | META_SHIFT_ON
+ | META_CTRL_LEFT_ON
+ | META_CTRL_RIGHT_ON
+ | META_CTRL_ON
+ | META_META_LEFT_ON
+ | META_META_RIGHT_ON
+ | META_META_ON)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, ModifierStateListener,
+ };
+ use crate::sticky_keys_filter::{
+ StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK,
+ KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON,
+ META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON,
+ META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON,
+ };
+ use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use binder::Strong;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
+ KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+ };
+ use std::sync::{Arc, RwLock};
+
+ static DEVICE_ID: i32 = 1;
+ static KEY_A: i32 = 29;
+ static BASE_KEY_DOWN: KeyEvent = KeyEvent {
+ id: 1,
+ deviceId: DEVICE_ID,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: Source::KEYBOARD,
+ displayId: 0,
+ policyFlags: 0,
+ action: KeyEventAction::DOWN,
+ flags: 0,
+ keyCode: 0,
+ scanCode: 0,
+ metaState: 0,
+ };
+
+ static BASE_KEY_UP: KeyEvent = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_DOWN };
+
+ #[test]
+ fn test_notify_key_consumes_ephemeral_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_ALT_LEFT,
+ KEYCODE_ALT_RIGHT,
+ KEYCODE_CTRL_LEFT,
+ KEYCODE_CTRL_RIGHT,
+ KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT,
+ KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT,
+ ];
+ for key_code in key_codes.iter() {
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN });
+ assert!(test_filter.last_event().is_none());
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_UP });
+ assert!(test_filter.last_event().is_none());
+ }
+ }
+
+ #[test]
+ fn test_notify_key_passes_non_ephemeral_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_CAPS_LOCK,
+ KEYCODE_NUM_LOCK,
+ KEYCODE_SCROLL_LOCK,
+ KEYCODE_FUNCTION,
+ KEYCODE_SYM,
+ ];
+ for key_code in key_codes.iter() {
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+ }
+
+ #[test]
+ fn test_notify_key_passes_non_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+
+ let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_modifier_state_updated_on_modifier_key_press() {
+ let mut test_filter = TestFilter::new();
+ let mut test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let test_states = &[
+ (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON),
+ (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON),
+ (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON),
+ (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON),
+ (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON),
+ (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON),
+ (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON),
+ (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON),
+ ];
+ for test_state in test_states.iter() {
+ test_filter.clear();
+ test_callbacks.clear();
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ // Re-send keys to lock it
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
+
+ // Re-send keys to clear
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+ }
+
+ #[test]
+ fn test_modifier_state_cleared_on_non_modifier_key_press() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+
+ #[test]
+ fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_UP });
+
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ }
+
+ #[test]
+ fn test_key_events_have_sticky_modifier_state() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ assert_eq!(
+ test_filter.last_event().unwrap().metaState as u32,
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+ assert_eq!(
+ test_filter.last_event().unwrap().metaState as u32,
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ }
+
+ #[test]
+ fn test_modifier_state_not_cleared_until_all_devices_removed() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 1,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_DOWN
+ });
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 1,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_UP
+ });
+
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 2,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_DOWN
+ });
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 2,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_UP
+ });
+
+ sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]);
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_devices_changed(&[]);
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+
+ fn setup_filter(
+ next: Box<dyn Filter + Send + Sync>,
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ ) -> StickyKeysFilter {
+ StickyKeysFilter::new(next, ModifierStateListener::new(callbacks))
+ }
+}
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 7668011..800f864 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -41,6 +41,7 @@
void setDisplayViewport(const DisplayViewport& viewport) override;
void updatePointerIcon(PointerIconStyle iconId) override;
void setCustomPointerIcon(const SpriteIcon& icon) override;
+ void fade(Transition) override;
void assertViewportSet(int32_t displayId);
void assertViewportNotSet();
@@ -56,7 +57,6 @@
std::string dump() override { return ""; }
std::optional<FloatRect> getBounds() const override;
void move(float deltaX, float deltaY) override;
- void fade(Transition) override;
void unfade(Transition) override;
void setPresentation(Presentation) override {}
void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5002391..ef07540 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -572,7 +572,8 @@
/** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
* essentially a passthrough for notifySwitch.
*/
- mLastNotifySwitch = NotifySwitchArgs(/*id=*/1, when, policyFlags, switchValues, switchMask);
+ mLastNotifySwitch =
+ NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask);
}
void pokeUserActivity(nsecs_t, int32_t, int32_t) override {
@@ -836,7 +837,8 @@
}
TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
- NotifySwitchArgs args(/*id=*/10, /*eventTime=*/20, /*policyFlags=*/0, /*switchValues=*/1,
+ NotifySwitchArgs args(InputEvent::nextId(), /*eventTime=*/20, /*policyFlags=*/0,
+ /*switchValues=*/1,
/*switchMask=*/2);
mDispatcher->notifySwitch(args);
@@ -1182,6 +1184,11 @@
mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
}
+ void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH,
+ shouldGlobalStylusBlockTouch);
+ }
+
void setAlpha(float alpha) { mInfo.alpha = alpha; }
void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
@@ -1221,31 +1228,27 @@
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
- KeyEvent* consumeKey(bool handled = true) {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
- if (event == nullptr) {
- ADD_FAILURE() << "Consume failed : no event";
- return nullptr;
+ const KeyEvent& consumeKey(bool handled = true) {
+ const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
+ if (event.getType() != InputEventType::KEY) {
+ LOG(FATAL) << "Instead of key event, got " << event;
}
- if (event->getType() != InputEventType::KEY) {
- ADD_FAILURE() << "Instead of key event, got " << *event;
- return nullptr;
- }
- return static_cast<KeyEvent*>(event);
+ return static_cast<const KeyEvent&>(event);
}
void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
- KeyEvent* keyEvent = consumeKey();
- ASSERT_NE(nullptr, keyEvent) << "Did not get a key event, but expected " << matcher;
- ASSERT_THAT(*keyEvent, matcher);
+ const KeyEvent& keyEvent = consumeKey();
+ ASSERT_THAT(keyEvent, matcher);
}
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags);
+ consumeKeyEvent(AllOf(WithKeyAction(ACTION_DOWN), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags);
+ consumeKeyEvent(AllOf(WithKeyAction(ACTION_UP), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
@@ -1267,44 +1270,46 @@
void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt,
std::optional<int32_t> expectedFlags = std::nullopt) {
- consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
- expectedFlags);
+ consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ testing::Conditional(expectedDisplayId.has_value(),
+ WithDisplayId(*expectedDisplayId), testing::_),
+ testing::Conditional(expectedFlags.has_value(), WithFlags(*expectedFlags),
+ testing::_)));
}
void consumeMotionPointerDown(int32_t pointerIdx,
int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
- expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, expectedDisplayId,
- expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE),
+ WithDisplayId(expectedDisplayId), WithFlags(expectedFlags)));
}
- void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- MotionEvent* motionEvent = consumeMotion();
- ASSERT_NE(nullptr, motionEvent);
- EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent->getActionMasked());
- EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getX());
- EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getY());
+ void consumeMotionOutsideWithZeroedCoords() {
+ consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), WithRawCoords(0, 0)));
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
@@ -1319,21 +1324,15 @@
mInputReceiver->consumeCaptureEvent(hasCapture);
}
- const MotionEvent& consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
- MotionEvent* motionEvent = consumeMotion();
- if (nullptr == motionEvent) {
- LOG(FATAL) << "Did not get a motion event, but expected " << matcher;
+ const MotionEvent& consumeMotionEvent(
+ const ::testing::Matcher<MotionEvent>& matcher = testing::_) {
+ const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event.getType() != InputEventType::MOTION) {
+ LOG(FATAL) << "Instead of motion event, got " << event;
}
- EXPECT_THAT(*motionEvent, matcher);
- return *motionEvent;
- }
-
- void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
- std::optional<int32_t> expectedDisplayId,
- std::optional<int32_t> expectedFlags) {
- ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
- mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
- expectedFlags);
+ const auto& motionEvent = static_cast<const MotionEvent&>(event);
+ EXPECT_THAT(motionEvent, matcher);
+ return motionEvent;
}
void consumeDragEvent(bool isExiting, float x, float y) {
@@ -1364,26 +1363,6 @@
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) {
- if (mInputReceiver == nullptr) {
- return nullptr;
- }
- return mInputReceiver->consume(timeout, handled);
- }
-
- MotionEvent* consumeMotion() {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- if (event == nullptr) {
- ADD_FAILURE() << "Consume failed : no event";
- return nullptr;
- }
- if (event->getType() != InputEventType::MOTION) {
- ADD_FAILURE() << "Instead of motion event, got " << *event;
- return nullptr;
- }
- return static_cast<MotionEvent*>(event);
- }
-
void assertNoEvents() {
if (mInputReceiver == nullptr &&
mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
@@ -1415,6 +1394,17 @@
std::shared_ptr<FakeInputReceiver> mInputReceiver;
static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
friend class sp<FakeWindowHandle>;
+
+ const InputEvent& consume(std::chrono::milliseconds timeout, bool handled = true) {
+ if (mInputReceiver == nullptr) {
+ LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
+ }
+ InputEvent* event = mInputReceiver->consume(timeout, handled);
+ if (event == nullptr) {
+ LOG(FATAL) << "Consume failed: no event";
+ }
+ return *event;
+ }
};
std::atomic<int32_t> FakeWindowHandle::sId{1};
@@ -1579,9 +1569,9 @@
static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, POLICY_FLAG_PASS_TO_USER, action, /*flags=*/0, AKEYCODE_A, KEY_A,
- AMETA_NONE, currentTime);
+ NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
+ AINPUT_SOURCE_KEYBOARD, displayId, POLICY_FLAG_PASS_TO_USER, action,
+ /*flags=*/0, AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
return args;
}
@@ -1590,9 +1580,9 @@
int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, AMETA_META_ON,
- currentTime);
+ NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
+ AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C,
+ AMETA_META_ON, currentTime);
return args;
}
@@ -1601,9 +1591,9 @@
int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, KEY_ASSISTANT,
- AMETA_NONE, currentTime);
+ NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
+ AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST,
+ KEY_ASSISTANT, AMETA_NONE, currentTime);
return args;
}
@@ -1631,9 +1621,9 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
- NotifyMotionArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, source, displayId,
- POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, /*flags=*/0,
- AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
+ NotifyMotionArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, source,
+ 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,
@@ -1652,7 +1642,8 @@
static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(
const PointerCaptureRequest& request) {
- return NotifyPointerCaptureChangedArgs(/*id=*/0, systemTime(SYSTEM_TIME_MONOTONIC), request);
+ return NotifyPointerCaptureChangedArgs(InputEvent::nextId(), systemTime(SYSTEM_TIME_MONOTONIC),
+ request);
}
} // namespace
@@ -3355,6 +3346,150 @@
}
/**
+ * If stylus is down anywhere on the screen, then touches should not be delivered to windows that
+ * have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH.
+ *
+ * Two windows: one on the left and one on the right.
+ * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config.
+ * Stylus down on the left window, and then touch down on the right window.
+ * Check that the right window doesn't get touches while the stylus is down on the left window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusDownBlocksTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+
+ sp<FakeWindowHandle> sbtRightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT);
+ sbtRightWindow->setFrame(Rect(100, 100, 200, 200));
+ sbtRightWindow->setGlobalStylusBlocksTouch(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 5;
+ const int32_t touchDeviceId = 4;
+
+ // Stylus down in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Finger tap on the right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+
+ // The touch should be blocked, because stylus is down somewhere else on screen!
+ sbtRightWindow->assertNoEvents();
+
+ // Continue stylus motion, and ensure it's not impacted.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithDeviceId(stylusDeviceId)));
+
+ // Now that the stylus gesture is done, touches should be getting delivered correctly.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153))
+ .deviceId(touchDeviceId)
+ .build());
+ sbtRightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
+ * If stylus is hovering anywhere on the screen, then touches should not be delivered to windows
+ * that have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH.
+ *
+ * Two windows: one on the left and one on the right.
+ * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config.
+ * Stylus hover on the left window, and then touch down on the right window.
+ * Check that the right window doesn't get touches while the stylus is hovering on the left window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusHoverBlocksTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+
+ sp<FakeWindowHandle> sbtRightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT);
+ sbtRightWindow->setFrame(Rect(100, 100, 200, 200));
+ sbtRightWindow->setGlobalStylusBlocksTouch(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 5;
+ const int32_t touchDeviceId = 4;
+
+ // Stylus hover in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Finger tap on the right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+
+ // The touch should be blocked, because stylus is hovering somewhere else on screen!
+ sbtRightWindow->assertNoEvents();
+
+ // Continue stylus motion, and ensure it's not impacted.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+
+ // Now that the stylus gesture is done, touches should be getting delivered correctly.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153))
+ .deviceId(touchDeviceId)
+ .build());
+ sbtRightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* A spy window above a window with no input channel.
* Start hovering with a stylus device, and then tap with it.
* Ensure spy window receives the entire sequence.
@@ -3625,20 +3760,18 @@
mDispatcher->waitForIdle();
- MotionEvent* motionEvent1 = window1->consumeMotion();
- ASSERT_NE(motionEvent1, nullptr);
+ const MotionEvent& motionEvent1 = window1->consumeMotionEvent();
window2->assertNoEvents();
- nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
- ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
+ nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
+ ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
// Now touch down on the window with another pointer
mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
mDispatcher->waitForIdle();
- MotionEvent* motionEvent2 = window2->consumeMotion();
- ASSERT_NE(motionEvent2, nullptr);
- nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
+ const MotionEvent& motionEvent2 = window2->consumeMotionEvent();
+ nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
- ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
+ ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
// Now move the pointer on the second window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
@@ -4233,8 +4366,7 @@
// When device reset happens, that key stream should be terminated with FLAG_CANCELED
// on the app side.
mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
- window->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
- AKEY_EVENT_FLAG_CANCELED);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT, AKEY_EVENT_FLAG_CANCELED);
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
@@ -4473,12 +4605,12 @@
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- const MotionEvent* event = window->consumeMotion();
- EXPECT_EQ(POINTER_1_DOWN, event->getAction());
- EXPECT_EQ(70, event->getX(0)); // 50 + 20
- EXPECT_EQ(90, event->getY(0)); // 50 + 40
- EXPECT_EQ(-10, event->getX(1)); // -30 + 20
- EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+ const MotionEvent& event = window->consumeMotionEvent();
+ EXPECT_EQ(POINTER_1_DOWN, event.getAction());
+ EXPECT_EQ(70, event.getX(0)); // 50 + 20
+ EXPECT_EQ(90, event.getY(0)); // 50 + 40
+ EXPECT_EQ(-10, event.getX(1)); // -30 + 20
+ EXPECT_EQ(-10, event.getY(1)); // -50 + 40
}
/**
@@ -4744,15 +4876,15 @@
EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindowDefaultDisplay->getToken()));
// windowDefaultDisplay gets cancel
- MotionEvent* event = windowDefaultDisplay->consumeMotion();
- EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event->getAction());
+ const MotionEvent& event = windowDefaultDisplay->consumeMotionEvent();
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
// The cancel event is sent to windowDefaultDisplay of the ADISPLAY_ID_DEFAULT display, so the
// coordinates of the cancel are converted by windowDefaultDisplay's transform, the x and y
// coordinates are both 100, otherwise if the cancel event is sent to windowSecondDisplay of
// SECOND_DISPLAY_ID, the x and y coordinates are 200
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(100, event->getY(0));
+ EXPECT_EQ(100, event.getX(0));
+ EXPECT_EQ(100, event.getY(0));
}
/**
@@ -4882,19 +5014,18 @@
{PointF{150, 220}}));
firstWindow->assertNoEvents();
- const MotionEvent* event = secondWindow->consumeMotion();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+ const MotionEvent& event = secondWindow->consumeMotionEvent();
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event.getAction());
// Ensure that the events from the "getRaw" API are in logical display coordinates.
- EXPECT_EQ(300, event->getRawX(0));
- EXPECT_EQ(880, event->getRawY(0));
+ EXPECT_EQ(300, event.getRawX(0));
+ EXPECT_EQ(880, event.getRawY(0));
// Ensure that the x and y values are in the window's coordinate space.
// The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
// the logical display space. This will be the origin of the window space.
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(80, event->getY(0));
+ EXPECT_EQ(100, event.getX(0));
+ EXPECT_EQ(80, event.getY(0));
}
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
@@ -5968,8 +6099,7 @@
motionArgs.pointerCoords[0].getX() - 10);
mDispatcher->notifyMotion(motionArgs);
- window->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
- /*expectedFlags=*/0);
+ window->consumeMotionMove(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0);
}
/**
@@ -6039,10 +6169,9 @@
const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
mDispatcher->notifyKey(keyArgs);
- KeyEvent* event = window->consumeKey();
- ASSERT_NE(event, nullptr);
+ const KeyEvent& event = window->consumeKey();
- std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+ std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event);
ASSERT_NE(verified, nullptr);
ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY);
@@ -6083,10 +6212,9 @@
ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(motionArgs);
- MotionEvent* event = window->consumeMotion();
- ASSERT_NE(event, nullptr);
+ const MotionEvent& event = window->consumeMotionEvent();
- std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+ std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event);
ASSERT_NE(verified, nullptr);
ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION);
@@ -6664,9 +6792,8 @@
}
void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) {
- KeyEvent* event = mWindow->consumeKey(handled);
- ASSERT_NE(event, nullptr) << "Did not receive key event";
- ASSERT_THAT(*event, matcher);
+ const KeyEvent& event = mWindow->consumeKey(handled);
+ ASSERT_THAT(event, matcher);
}
};
@@ -6897,7 +7024,7 @@
mDispatcher->notifyKey(keyArgs);
// Window should receive key down event.
- mWindow->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+ mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT,
/*expectedFlags=*/0);
}
};
@@ -6967,10 +7094,9 @@
GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- KeyEvent* repeatEvent = mWindow->consumeKey();
- ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
+ const KeyEvent& repeatEvent = mWindow->consumeKey();
EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER,
- IdGenerator::getSource(repeatEvent->getId()));
+ IdGenerator::getSource(repeatEvent.getId()));
}
}
@@ -6980,9 +7106,8 @@
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- KeyEvent* repeatEvent = mWindow->consumeKey();
- ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
- int32_t id = repeatEvent->getId();
+ const KeyEvent& repeatEvent = mWindow->consumeKey();
+ int32_t id = repeatEvent.getId();
EXPECT_EQ(idSet.end(), idSet.find(id));
idSet.insert(id);
}
@@ -7071,8 +7196,7 @@
mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
// Old focus should receive a cancel event.
- windowInSecondary->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
- AKEY_EVENT_FLAG_CANCELED);
+ windowInSecondary->consumeKeyUp(ADISPLAY_ID_NONE, AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
@@ -7550,24 +7674,21 @@
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
const std::vector<PointF>& points) {
const std::string name = window->getName();
- MotionEvent* motionEvent = window->consumeMotion();
+ const MotionEvent& motionEvent =
+ window->consumeMotionEvent(WithMotionAction(expectedAction));
- ASSERT_NE(nullptr, motionEvent)
- << name.c_str() << ": consumer should have returned non-NULL event.";
-
- ASSERT_THAT(*motionEvent, WithMotionAction(expectedAction));
- ASSERT_EQ(points.size(), motionEvent->getPointerCount());
+ ASSERT_EQ(points.size(), motionEvent.getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
float expectedY = points[i].y;
- EXPECT_EQ(expectedX, motionEvent->getX(i))
+ EXPECT_EQ(expectedX, motionEvent.getX(i))
<< "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent->getX(i);
- EXPECT_EQ(expectedY, motionEvent->getY(i))
+ << ", got " << motionEvent.getX(i);
+ EXPECT_EQ(expectedY, motionEvent.getY(i))
<< "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent->getY(i);
+ << ", got " << motionEvent.getY(i);
}
}
@@ -8380,8 +8501,7 @@
.build()));
mFocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionUp();
- mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
- ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
// We consumed all events, so no ANR
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -8457,8 +8577,7 @@
// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
tapOnFocusedWindow();
- mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
- ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
// Receive the events, but don't respond
std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(downEventSequenceNum);
@@ -8590,8 +8709,7 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{FOCUSED_WINDOW_LOCATION}));
- mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
- ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
// Touch Window 2
mDispatcher->notifyMotion(
@@ -9723,7 +9841,7 @@
// Receives cancel for first pointer after next pointer down
mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
- mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ mSpyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithPointerIds({1})));
mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
mSpyWindow->assertNoEvents();
@@ -9733,13 +9851,13 @@
mDragWindow->assertNoEvents();
const MotionEvent firstFingerMoveEvent =
- MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(60).y(60))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, firstFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -9930,8 +10048,7 @@
.displayId(SECOND_DISPLAY_ID)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- windowInSecondary->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
- SECOND_DISPLAY_ID, /*expectedFlag=*/0);
+ windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID, /*expectedFlag=*/0);
// Update window again.
mDispatcher->onWindowInfosChanged(
{{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index e0a3e94..6816818 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2894,9 +2894,9 @@
mapper.assertConfigureWasCalled();
mapper.assertResetWasNotCalled();
- RawEvent event{.deviceId = EVENTHUB_ID,
+ RawEvent event{.when = ARBITRARY_TIME,
.readTime = ARBITRARY_TIME,
- .when = ARBITRARY_TIME,
+ .deviceId = EVENTHUB_ID,
.type = EV_SYN,
.code = SYN_REPORT,
.value = 0};
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 2457f7c..193b84d 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -17,6 +17,7 @@
#include "../PointerChoreographer.h"
#include <gtest/gtest.h>
+#include <deque>
#include <vector>
#include "FakePointerController.h"
@@ -33,7 +34,9 @@
// Helpers to std::visit with lambdas.
template <typename... V>
-struct Visitor : V... {};
+struct Visitor : V... {
+ using V::operator()...;
+};
template <typename... V>
Visitor(V...) -> Visitor<V...>;
@@ -88,14 +91,14 @@
std::shared_ptr<FakePointerController> assertPointerControllerCreated(
ControllerType expectedType) {
- EXPECT_TRUE(mLastCreatedController) << "No PointerController was created";
- auto [type, controller] = std::move(*mLastCreatedController);
+ EXPECT_FALSE(mCreatedControllers.empty()) << "No PointerController was created";
+ auto [type, controller] = std::move(mCreatedControllers.front());
EXPECT_EQ(expectedType, type);
- mLastCreatedController.reset();
+ mCreatedControllers.pop_front();
return controller;
}
- void assertPointerControllerNotCreated() { ASSERT_EQ(std::nullopt, mLastCreatedController); }
+ void assertPointerControllerNotCreated() { ASSERT_TRUE(mCreatedControllers.empty()); }
void assertPointerControllerRemoved(const std::shared_ptr<FakePointerController>& pc) {
// Ensure that the code under test is not holding onto this PointerController.
@@ -110,6 +113,12 @@
"to this PointerController";
}
+ void assertPointerControllerNotRemoved(const std::shared_ptr<FakePointerController>& pc) {
+ // See assertPointerControllerRemoved above.
+ ASSERT_GT(pc.use_count(), 1) << "Expected PointerChoreographer to hold at least one "
+ "reference to this PointerController";
+ }
+
void assertPointerDisplayIdNotified(int32_t displayId) {
ASSERT_EQ(displayId, mPointerDisplayIdNotified);
mPointerDisplayIdNotified.reset();
@@ -118,17 +127,15 @@
void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); }
private:
- std::optional<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
- mLastCreatedController;
+ std::deque<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
+ mCreatedControllers;
std::optional<int32_t> mPointerDisplayIdNotified;
std::shared_ptr<PointerControllerInterface> createPointerController(
ControllerType type) override {
- EXPECT_FALSE(mLastCreatedController.has_value())
- << "More than one PointerController created at a time";
std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
EXPECT_FALSE(pc->isPointerShown());
- mLastCreatedController = {type, pc};
+ mCreatedControllers.emplace_back(type, pc);
return pc;
}
@@ -184,33 +191,15 @@
}
}
-TEST_F(PointerChoreographerTest, WhenMouseIsJustAddedDoesNotCreatePointerController) {
+TEST_F(PointerChoreographerTest, WhenMouseIsAddedCreatesPointerController) {
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- assertPointerControllerNotCreated();
-}
-
-TEST_F(PointerChoreographerTest, WhenMouseEventOccursCreatesPointerController) {
- mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
}
TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) {
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
// Remove the mouse.
@@ -226,34 +215,20 @@
}
TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) {
- // Just adding a viewport or device should not create a PointerController.
+ // Just adding a viewport or device should create a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
- assertPointerControllerNotCreated();
- // After the mouse emits event, PointerController will be created and viewport will be set.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
}
TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) {
- // Without viewport information, PointerController will be created by a mouse event
- // but viewport won't be set.
+ // Without viewport information, PointerController will be created but viewport won't be set.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportNotSet();
@@ -270,14 +245,9 @@
// the PointerController.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
}
TEST_F(PointerChoreographerTest,
@@ -287,29 +257,18 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
firstDisplayPc->assertViewportSet(DISPLAY_ID);
+ ASSERT_TRUE(firstDisplayPc->isPointerShown());
- // Change default mouse display. Existing PointerController should be removed.
+ // Change default mouse display. Existing PointerController should be removed and a new one
+ // should be created.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
- assertPointerControllerNotCreated();
- // New PointerController for the new default display will be created by the motion event.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID);
+ ASSERT_TRUE(secondDisplayPc->isPointerShown());
}
TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
@@ -317,12 +276,6 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -332,12 +285,6 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotNotified();
@@ -350,12 +297,6 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -372,28 +313,13 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
- // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
- // before a mouse event.
+ // Set another viewport as a default mouse display ID. The mouse is moved to the other display.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
- assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
assertPointerControllerRemoved(firstDisplayPc);
- // After a mouse event, pointer display ID will be notified with new default mouse display.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
}
@@ -403,13 +329,6 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -428,7 +347,7 @@
pc->assertPosition(110, 220);
ASSERT_TRUE(pc->isPointerShown());
- // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
}
@@ -444,23 +363,8 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
-
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(SECOND_DEVICE_ID)
- .displayId(ANOTHER_DISPLAY_ID)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
@@ -483,7 +387,7 @@
ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
ASSERT_TRUE(associatedMousePc->isPointerShown());
- // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID),
WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420)));
@@ -494,13 +398,6 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -531,7 +428,7 @@
pc->assertPosition(100, 200);
ASSERT_FALSE(pc->isPointerShown());
- // Check x-y cooridnates, displayId and cursor position are not changed.
+ // Check x-y coordinates, displayId and cursor position are not changed.
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithCoords(10, 20), WithRelativeMotion(10, 20), WithDisplayId(ADISPLAY_ID_NONE),
WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
@@ -543,13 +440,6 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
ASSERT_TRUE(pc->isPointerShown());
@@ -561,6 +451,65 @@
ASSERT_FALSE(pc->isPointerShown());
}
+TEST_F(PointerChoreographerTest, MultipleMiceConnectionAndRemoval) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // A mouse is connected, and the pointer is shown.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ pc->fade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ // Add a second mouse is added, the pointer is shown again.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // One of the mice is removed, and it does not cause the mouse pointer to fade, because
+ // we have one more mouse connected.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotRemoved(pc);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // The final mouse is removed. The pointer is removed.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ pc->fade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ // Adding a touchscreen device does not unfade the mouse pointer.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+ DISPLAY_ID)}});
+
+ ASSERT_FALSE(pc->isPointerShown());
+
+ // Show touches setting change does not unfade the mouse pointer.
+ mChoreographer.setShowTouchesEnabled(true);
+
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
TEST_F(PointerChoreographerTest, WhenShowTouchesEnabledAndDisabledDoesNotCreatePointerController) {
// Disable show touches and add a touch device.
mChoreographer.setShowTouchesEnabled(false);
@@ -1038,25 +987,11 @@
assertPointerControllerRemoved(pc);
}
-TEST_F(PointerChoreographerTest, WhenTouchpadIsJustAddedDoesNotCreatePointerController) {
+TEST_F(PointerChoreographerTest, WhenTouchpadIsAddedCreatesPointerController) {
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- assertPointerControllerNotCreated();
-}
-
-TEST_F(PointerChoreographerTest, WhenTouchpadEventOccursCreatesPointerController) {
- mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
}
@@ -1065,12 +1000,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
// Remove the touchpad.
@@ -1085,15 +1014,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
DISPLAY_ID)}});
- assertPointerControllerNotCreated();
-
- // After the touchpad emits event, PointerController will be created and viewport will be set.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
}
@@ -1105,12 +1025,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
DISPLAY_ID)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportNotSet();
@@ -1127,32 +1041,19 @@
// the PointerController.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
}
TEST_F(PointerChoreographerTest,
WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) {
- // Set one display as a default touchpad display and emit touchpad event to create
- // PointerController.
+ // Set one display as a default touchpad display and create PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
firstDisplayPc->assertViewportSet(DISPLAY_ID);
@@ -1160,13 +1061,6 @@
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
- // New PointerController for the new default display will be created by the motion event.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID);
}
@@ -1178,12 +1072,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -1195,12 +1083,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotNotified();
@@ -1215,12 +1097,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -1240,28 +1116,14 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
// Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
// before a touchpad event.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
- assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
assertPointerControllerRemoved(firstDisplayPc);
- // After a touchpad event, pointer display ID will be notified with new default mouse display.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
}
@@ -1273,13 +1135,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1298,7 +1153,7 @@
pc->assertPosition(110, 220);
ASSERT_TRUE(pc->isPointerShown());
- // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
}
@@ -1310,13 +1165,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1398,23 +1246,8 @@
ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ANOTHER_DISPLAY_ID)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
-
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(SECOND_DEVICE_ID)
- .displayId(ANOTHER_DISPLAY_ID)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
@@ -1437,7 +1270,7 @@
ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
ASSERT_TRUE(associatedMousePc->isPointerShown());
- // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID),
WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420)));
@@ -1450,13 +1283,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1479,7 +1305,7 @@
pc->assertPosition(200, 300);
ASSERT_FALSE(pc->isPointerShown());
- // Check x-y cooridnates, displayId and cursor position are not changed.
+ // Check x-y coordinates, displayId and cursor position are not changed.
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithCoords(100, 200), WithDisplayId(ADISPLAY_ID_NONE),
WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
@@ -1493,13 +1319,6 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(TOUCHPAD_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
ASSERT_TRUE(pc->isPointerShown());
@@ -1517,12 +1336,6 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1537,12 +1350,6 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1558,12 +1365,6 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1573,41 +1374,12 @@
pc->assertPointerIconNotSet();
}
-TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForDeviceWithoutPointerController) {
- // Add two devices, one with a PointerController and the other without PointerController.
- mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
- mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
- generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
- pc->assertPointerIconNotSet();
-
- // Set pointer icon for the device without PointerController. This should be ignored.
- ASSERT_FALSE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, ANOTHER_DISPLAY_ID,
- SECOND_DEVICE_ID));
- pc->assertPointerIconNotSet();
-}
-
TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertCustomPointerIconNotSet();
@@ -1632,20 +1404,8 @@
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId());
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(SECOND_DEVICE_ID)
- .displayId(ANOTHER_DISPLAY_ID)
- .build());
auto secondMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(ANOTHER_DISPLAY_ID, secondMousePc->getDisplayId());
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index 66fdaa4..8ba497a 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -464,6 +464,53 @@
return WithPointersMatcher(pointers);
}
+/// Pointer ids matcher
+class WithPointerIdsMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithPointerIdsMatcher(std::set<int32_t> pointerIds) : mPointerIds(pointerIds) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+ std::set<int32_t> actualPointerIds;
+ for (size_t pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) {
+ const PointerProperties* properties = event.getPointerProperties(pointerIndex);
+ actualPointerIds.insert(properties->id);
+ }
+
+ if (mPointerIds != actualPointerIds) {
+ *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got "
+ << dumpSet(actualPointerIds);
+ return false;
+ }
+ return true;
+ }
+
+ bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+ std::set<int32_t> actualPointerIds;
+ for (const PointerProperties& properties : event.pointerProperties) {
+ actualPointerIds.insert(properties.id);
+ }
+
+ if (mPointerIds != actualPointerIds) {
+ *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got "
+ << dumpSet(actualPointerIds);
+ return false;
+ }
+ return true;
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with pointer ids " << dumpSet(mPointerIds); }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer ids"; }
+
+private:
+ const std::set<int32_t> mPointerIds;
+};
+
+inline WithPointerIdsMatcher WithPointerIds(const std::set<int32_t /*id*/>& pointerIds) {
+ return WithPointerIdsMatcher(pointerIds);
+}
+
/// Key code
class WithKeyCodeMatcher {
public:
diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
index 0bd8fb3..dc5a213 100644
--- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -65,6 +65,27 @@
std::map<int32_t /*displayId*/, InputVerifier> mVerifiers;
};
+void scrambleWindow(FuzzedDataProvider& fdp, FakeWindowHandle& window) {
+ const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+
+ window.setFrame(Rect(left, top, left + width, top + height));
+ window.setSlippery(fdp.ConsumeBool());
+ window.setDupTouchToWallpaper(fdp.ConsumeBool());
+ window.setIsWallpaper(fdp.ConsumeBool());
+ window.setVisible(fdp.ConsumeBool());
+ window.setPreventSplitting(fdp.ConsumeBool());
+ const bool isTrustedOverlay = fdp.ConsumeBool();
+ window.setTrustedOverlay(isTrustedOverlay);
+ if (isTrustedOverlay) {
+ window.setSpy(fdp.ConsumeBool());
+ } else {
+ window.setSpy(false);
+ }
+}
+
} // namespace
sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatcher& dispatcher,
@@ -73,17 +94,9 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
std::string windowName = android::base::StringPrintf("Win") + std::to_string(windowNumber++);
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, dispatcher, "Fake", displayId);
+ sp<FakeWindowHandle>::make(application, dispatcher, windowName, displayId);
- const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
- const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
- const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
- const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
-
- window->setFrame(Rect(left, top, left + width, top + height));
- window->setSlippery(fdp.ConsumeBool());
- window->setDupTouchToWallpaper(fdp.ConsumeBool());
- window->setTrustedOverlay(fdp.ConsumeBool());
+ scrambleWindow(fdp, *window);
return window;
}
@@ -113,7 +126,14 @@
windowsPerDisplay.erase(displayId);
}
},
- // Could also clone a window, change flags, reposition, etc...
+ // Change flags or move some of the existing windows
+ [&]() -> void {
+ for (auto& window : windows) {
+ if (fdp.ConsumeBool()) {
+ scrambleWindow(fdp, *window);
+ }
+ }
+ },
})();
}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 641ba9f..3d2cf29 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -29,9 +29,12 @@
#include <thread>
using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::ChannelConfig;
using aidl::android::hardware::power::IPower;
using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::SessionConfig;
+using aidl::android::hardware::power::SessionTag;
using android::binder::Status;
using namespace android;
@@ -53,6 +56,14 @@
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, SessionTag tag, SessionConfig* config,
+ std::shared_ptr<IPowerHintSession>* _aidl_return),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
+ (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 11c56a8..0dd4dd6 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -7,6 +7,18 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "sensorservice_flags",
+ package: "com.android.frameworks.sensorservice.flags",
+ srcs: ["senserservice_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "sensorservice_flags_c_lib",
+ aconfig_declarations: "dynamic_sensors_flags",
+ host_supported: true,
+}
+
cc_library {
name: "libsensorservice",
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 44d0d70..85043c9 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -91,7 +91,7 @@
std::map<String16, int> SensorService::sPackageTargetVersion;
Mutex SensorService::sPackageTargetVersionLock;
String16 SensorService::sSensorInterfaceDescriptorPrefix =
- String16("android.frameworks.sensorservice@");
+ String16("android.frameworks.sensorservice");
AppOpsManager SensorService::sAppOpsManager;
std::atomic_uint64_t SensorService::curProxCallbackSeq(0);
std::atomic_uint64_t SensorService::completedCallbackSeq(0);
@@ -2295,10 +2295,12 @@
}
int SensorService::getTargetSdkVersion(const String16& opPackageName) {
- // Don't query the SDK version for the ISensorManager descriptor as it doesn't have one. This
- // descriptor tends to be used for VNDK clients, but can technically be set by anyone so don't
- // give it elevated privileges.
- if (opPackageName.startsWith(sSensorInterfaceDescriptorPrefix)) {
+ // Don't query the SDK version for the ISensorManager descriptor as it
+ // doesn't have one. This descriptor tends to be used for VNDK clients, but
+ // can technically be set by anyone so don't give it elevated privileges.
+ bool isVNDK = opPackageName.startsWith(sSensorInterfaceDescriptorPrefix) &&
+ opPackageName.contains(String16("@"));
+ if (isVNDK) {
return -1;
}
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index 2b6ea7c..b6acc8a 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -188,8 +188,16 @@
}
::android::SensorManager& SensorManagerAidl::getInternalManager() {
- return ::android::SensorManager::getInstanceForPackage(
- String16(ISensorManager::descriptor));
+ int32_t version;
+ ndk::ScopedAStatus status = getInterfaceVersion(&version);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to get interface version with error: "
+ << status.getDescription();
+ version = -1;
+ }
+ String16 packageName = String16(ISensorManager::descriptor);
+ packageName += String16("@") + String16(std::to_string(version).c_str());
+ return ::android::SensorManager::getInstanceForPackage(packageName);
}
/* One global looper for all event queues created from this SensorManager. */
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig
new file mode 100644
index 0000000..a3bd0ee
--- /dev/null
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -0,0 +1,15 @@
+package: "com.android.frameworks.sensorservice.flags"
+
+flag {
+ name: "dynamic_sensor_hal_reconnect_handling"
+ namespace: "sensors"
+ description: "This flag controls if the dynamic sensor data will be clean up after HAL is disconnected."
+ bug: "307782607"
+}
+
+flag {
+ name: "sensor_device_on_dynamic_sensor_disconnected"
+ namespace: "sensors"
+ description: "This flag controls if the callback onDynamicSensorsDisconnected is implemented or not."
+ bug: "316958439"
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 5e84be1..c71c517 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -40,6 +40,9 @@
// True if the display is secure
virtual bool isSecure() const = 0;
+ // Sets the secure flag for the display
+ virtual void setSecure(bool secure) = 0;
+
// True if the display is virtual
virtual bool isVirtual() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index eac5d97..c53b461 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -74,6 +74,7 @@
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
void createClientCompositionCache(uint32_t cacheSize) override;
void applyDisplayBrightness(const bool applyImmediately) override;
+ void setSecure(bool secure) override;
// Internal helpers used by chooseCompositionStrategy()
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 7e99ec2..46cb95e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -33,6 +33,7 @@
MOCK_CONST_METHOD0(getId, DisplayId());
MOCK_CONST_METHOD0(isSecure, bool());
+ MOCK_METHOD1(setSecure, void(bool));
MOCK_CONST_METHOD0(isVirtual, bool());
MOCK_CONST_METHOD0(getPreferredBootHwcConfigId, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 690d35f..d907bf5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -74,6 +74,10 @@
return getState().isSecure;
}
+void Display::setSecure(bool secure) {
+ editState().isSecure = secure;
+}
+
bool Display::isVirtual() const {
return VirtualDisplayId::tryCast(mId).has_value();
}
@@ -246,7 +250,6 @@
}
// Get any composition changes requested by the HWC device, and apply them.
- std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
const bool requiresClientComposition = anyLayersRequireClientComposition();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 8b736be..9e35717 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -148,8 +148,7 @@
MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
- MOCK_METHOD(status_t, notifyExpectedPresentIfRequired,
- (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>));
+ MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index bf7ed87..30ba9e4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -4094,7 +4094,11 @@
};
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = false;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
@@ -4108,7 +4112,11 @@
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
@@ -4130,7 +4138,11 @@
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
@@ -4143,7 +4155,11 @@
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
@@ -4222,6 +4238,7 @@
GenerateClientCompositionRequestsTest() {
mOutput.mState.needsFiltering = false;
+ mOutput.mState.isProtected = true;
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
@@ -4246,6 +4263,7 @@
mOutput.mState.displaySpace.setOrientation(kDisplayOrientation);
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = false;
+ mOutput.mState.isProtected = true;
for (size_t i = 0; i < mLayers.size(); i++) {
mLayers[i].mOutputLayerState.clearClientTarget = false;
@@ -4708,7 +4726,7 @@
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
@@ -4721,7 +4739,7 @@
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
@@ -4734,7 +4752,7 @@
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
@@ -4912,7 +4930,7 @@
Region(Rect(0, 0, 1000, 1000)),
false, /* needs filtering */
true, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kPortraitViewport,
kOutputDataspace,
true /* realContentIsVisible */,
@@ -4931,7 +4949,7 @@
Region(Rect(1000, 0, 2000, 1000)),
false, /* needs filtering */
true, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kPortraitViewport,
kOutputDataspace,
true /* realContentIsVisible */,
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 950b05e..7b0aad7 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -222,7 +222,6 @@
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline& outTimeline) {
mPendingModeOpt = std::move(desiredMode);
- mIsModeSetPending = true;
const auto& mode = *mPendingModeOpt->mode.modePtr;
@@ -235,9 +234,22 @@
return true;
}
-void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
- setActiveMode(modeId, vsyncRate, renderFps);
- mIsModeSetPending = false;
+auto DisplayDevice::finalizeModeChange() -> ModeChange {
+ if (!mPendingModeOpt) return NoModeChange{"No pending mode"};
+
+ auto pendingMode = *std::exchange(mPendingModeOpt, std::nullopt);
+ auto& pendingModePtr = pendingMode.mode.modePtr;
+
+ if (!mRefreshRateSelector->displayModes().contains(pendingModePtr->getId())) {
+ return NoModeChange{"Unknown pending mode"};
+ }
+
+ if (getActiveMode().modePtr->getResolution() != pendingModePtr->getResolution()) {
+ return ResolutionChange{std::move(pendingMode)};
+ }
+
+ setActiveMode(pendingModePtr->getId(), pendingModePtr->getVsyncRate(), pendingMode.mode.fps);
+ return RefreshRateChange{std::move(pendingMode)};
}
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -353,6 +365,10 @@
return mCompositionDisplay->isSecure();
}
+void DisplayDevice::setSecure(bool secure) {
+ mCompositionDisplay->setSecure(secure);
+}
+
const Rect DisplayDevice::getBounds() const {
return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
}
@@ -571,10 +587,14 @@
return mDesiredModeOpt;
}
-void DisplayDevice::clearDesiredMode() {
- std::scoped_lock lock(mDesiredModeLock);
- mDesiredModeOpt.reset();
- mHasDesiredModeTrace = false;
+auto DisplayDevice::takeDesiredMode() -> DisplayModeRequestOpt {
+ DisplayModeRequestOpt desiredModeOpt;
+ {
+ std::scoped_lock lock(mDesiredModeLock);
+ std::swap(mDesiredModeOpt, desiredModeOpt);
+ mHasDesiredModeTrace = false;
+ }
+ return desiredModeOpt;
}
void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ac390cb..4c09ffd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <variant>
#include <android-base/thread_annotations.h>
#include <android/native_window.h>
@@ -95,6 +96,7 @@
// isSecure indicates whether this display can be trusted to display
// secure surfaces.
bool isSecure() const;
+ void setSecure(bool secure);
int getWidth() const;
int getHeight() const;
@@ -194,12 +196,11 @@
using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>;
DisplayModeRequestOpt getDesiredMode() const EXCLUDES(mDesiredModeLock);
- void clearDesiredMode() EXCLUDES(mDesiredModeLock);
+ DisplayModeRequestOpt takeDesiredMode() EXCLUDES(mDesiredModeLock);
- DisplayModeRequestOpt getPendingMode() const REQUIRES(kMainThreadContext) {
- return mPendingModeOpt;
+ bool isModeSetPending() const REQUIRES(kMainThreadContext) {
+ return mPendingModeOpt.has_value();
}
- bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; }
scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
return mRefreshRateSelector->getActiveMode();
@@ -211,8 +212,25 @@
hal::VsyncPeriodChangeTimeline& outTimeline)
REQUIRES(kMainThreadContext);
- void finalizeModeChange(DisplayModeId, Fps vsyncRate, Fps renderFps)
- REQUIRES(kMainThreadContext);
+ struct NoModeChange {
+ const char* reason;
+ };
+
+ struct ResolutionChange {
+ display::DisplayModeRequest activeMode;
+ };
+
+ struct RefreshRateChange {
+ display::DisplayModeRequest activeMode;
+ };
+
+ using ModeChange = std::variant<NoModeChange, ResolutionChange, RefreshRateChange>;
+
+ // Clears the pending DisplayModeRequest, and returns the ModeChange that occurred. If it was a
+ // RefreshRateChange, the pending mode becomes the active mode. If it was a ResolutionChange,
+ // the caller is responsible for resizing the framebuffer to match the active resolution by
+ // recreating the DisplayDevice.
+ ModeChange finalizeModeChange() REQUIRES(kMainThreadContext);
scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
@@ -249,6 +267,8 @@
void dump(utils::Dumper&) const;
private:
+ friend class TestableSurfaceFlinger;
+
template <size_t N>
inline std::string concatId(const char (&str)[N]) const {
return std::string(ftl::Concat(str, ' ', getId().value).str());
@@ -299,12 +319,15 @@
// This parameter is only used for hdr/sdr ratio overlay
float mHdrSdrRatio = 1.0f;
+ // A DisplayModeRequest flows through three states: desired, pending, and active. Requests
+ // within a frame are merged into a single desired request. Unless cleared, the request is
+ // relayed to HWC on the next frame, and becomes pending. The mode becomes active once HWC
+ // signals the present fence to confirm the mode set.
mutable std::mutex mDesiredModeLock;
DisplayModeRequestOpt mDesiredModeOpt GUARDED_BY(mDesiredModeLock);
TracedOrdinal<bool> mHasDesiredModeTrace GUARDED_BY(mDesiredModeLock);
DisplayModeRequestOpt mPendingModeOpt GUARDED_BY(kMainThreadContext);
- bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index c25f9dd..64a8ae7 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -271,7 +271,10 @@
}
}
}
-
+ if (getLayerLifecycleBatchCommand()) {
+ mEnableLayerCommandBatchingFlag =
+ FlagManager::getInstance().enable_layer_command_batching();
+ }
ALOGI("Loaded AIDL composer3 HAL service");
}
@@ -288,7 +291,7 @@
}
}
-bool AidlComposer::getDisplayConfigurationsSupported() const {
+bool AidlComposer::isVrrSupported() const {
return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config();
}
@@ -407,25 +410,58 @@
Error AidlComposer::createLayer(Display display, Layer* outLayer) {
int64_t layer;
- const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display),
- kMaxLayerBufferCount, &layer);
- if (!status.isOk()) {
- ALOGE("createLayer failed %s", status.getDescription().c_str());
- return static_cast<Error>(status.getServiceSpecificError());
+ Error error = Error::NONE;
+ if (!mEnableLayerCommandBatchingFlag) {
+ const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display),
+ kMaxLayerBufferCount, &layer);
+ if (!status.isOk()) {
+ ALOGE("createLayer failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ } else {
+ // generate a unique layerID. map in AidlComposer with <SF_layerID, HWC_layerID>
+ // Add this as a new displayCommand in execute command.
+ // return the SF generated layerID instead of calling HWC
+ layer = mLayerID++;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerLifecycleBatchCommandType(translate<int64_t>(display),
+ translate<int64_t>(layer),
+ LayerLifecycleBatchCommandType::CREATE);
+ writer->get().setNewBufferSlotCount(translate<int64_t>(display),
+ translate<int64_t>(layer), kMaxLayerBufferCount);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
}
-
*outLayer = translate<Layer>(layer);
- return Error::NONE;
+ return error;
}
Error AidlComposer::destroyLayer(Display display, Layer layer) {
- const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display),
- translate<int64_t>(layer));
- if (!status.isOk()) {
- ALOGE("destroyLayer failed %s", status.getDescription().c_str());
- return static_cast<Error>(status.getServiceSpecificError());
+ Error error = Error::NONE;
+ if (!mEnableLayerCommandBatchingFlag) {
+ const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display),
+ translate<int64_t>(layer));
+ if (!status.isOk()) {
+ ALOGE("destroyLayer failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ } else {
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get()
+ .setLayerLifecycleBatchCommandType(translate<int64_t>(display),
+ translate<int64_t>(layer),
+ LayerLifecycleBatchCommandType::DESTROY);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
}
- return Error::NONE;
+
+ return error;
}
Error AidlComposer::getActiveConfig(Display display, Config* outConfig) {
@@ -591,6 +627,13 @@
return Error::NONE;
}
+bool AidlComposer::getLayerLifecycleBatchCommand() {
+ std::vector<Capability> capabilities = getCapabilities();
+ bool hasCapability = std::find(capabilities.begin(), capabilities.end(),
+ Capability::LAYER_LIFECYCLE_BATCH_COMMAND) != capabilities.end();
+ return hasCapability;
+}
+
Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) {
const auto status = mAidlComposerClient->getOverlaySupport(outProperties);
if (!status.isOk()) {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 51ac1f5..ea0e53a 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -66,7 +66,7 @@
~AidlComposer() override;
bool isSupported(OptionalFeature) const;
- bool getDisplayConfigurationsSupported() const;
+ bool isVrrSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
@@ -262,7 +262,7 @@
void removeDisplay(Display) EXCLUDES(mMutex);
void addReader(Display) REQUIRES(mMutex);
void removeReader(Display) REQUIRES(mMutex);
-
+ bool getLayerLifecycleBatchCommand();
bool hasMultiThreadedPresentSupport(Display);
// 64KiB minus a small space for metadata such as read/write pointers
@@ -293,6 +293,8 @@
ftl::SharedMutex mMutex;
int32_t mComposerInterfaceVersion = 1;
+ bool mEnableLayerCommandBatchingFlag = false;
+ std::atomic<int64_t> mLayerID = 1;
// Buffer slots for layers are cleared by setting the slot buffer to this buffer.
sp<GraphicBuffer> mClearSlotBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 1a24222..bc067a0 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -105,7 +105,7 @@
};
virtual bool isSupported(OptionalFeature) const = 0;
- virtual bool getDisplayConfigurationsSupported() const = 0;
+ virtual bool isVrrSupported() const = 0;
virtual std::vector<aidl::android::hardware::graphics::composer3::Capability>
getCapabilities() = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 6b67865..3ffd8ea 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -15,6 +15,7 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <chrono>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -78,59 +79,6 @@
using aidl::android::hardware::graphics::composer3::DisplayCapability;
namespace hal = android::hardware::graphics::composer::hal;
-namespace {
-bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime,
- android::TimePoint lastExpectedPresentTimestamp,
- android::Fps lastFrameInterval, android::Period timeout,
- android::Duration threshold) {
- if (lastFrameInterval.getPeriodNsecs() == 0) {
- return false;
- }
-
- const auto expectedPresentTimeDeltaNs =
- expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns();
-
- if (expectedPresentTimeDeltaNs > timeout.ns()) {
- return false;
- }
-
- const auto expectedPresentPeriods = static_cast<nsecs_t>(
- std::round(static_cast<float>(expectedPresentTimeDeltaNs) /
- static_cast<float>(lastFrameInterval.getPeriodNsecs())));
- const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods;
- const auto calculatedExpectedPresentTimeNs =
- lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs;
- const auto presentTimeDelta =
- std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs);
- return presentTimeDelta < threshold.ns();
-}
-
-bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime,
- android::TimePoint lastExpectedPresentTimestamp,
- std::optional<android::Period> timeoutOpt,
- android::Duration threshold) {
- if (!timeoutOpt) {
- // Always within timeout if timeoutOpt is absent and don't send hint
- // for the timeout
- return true;
- }
-
- if (timeoutOpt->ns() == 0) {
- // Always outside timeout if timeoutOpt is 0 and always send
- // the hint for the timeout.
- return false;
- }
-
- if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) {
- return true;
- }
-
- // Check if within the threshold as it can be just outside the timeout
- return std::abs(expectedPresentTime.ns() -
- (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns();
-}
-} // namespace
-
namespace android {
HWComposer::~HWComposer() = default;
@@ -321,7 +269,7 @@
const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId();
- if (mComposer->getDisplayConfigurationsSupported()) {
+ if (mComposer->isVrrSupported()) {
return getModesFromDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs);
}
@@ -538,13 +486,6 @@
}();
displayData.validateWasSkipped = false;
- {
- std::scoped_lock lock{displayData.expectedPresentLock};
- if (expectedPresentTime > displayData.lastExpectedPresentTimestamp.ns()) {
- displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime);
- }
- }
-
ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue());
if (canSkipValidate) {
sp<Fence> outPresentFence = Fence::NO_FENCE;
@@ -939,55 +880,15 @@
return NO_ERROR;
}
-status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
- Period vsyncPeriod,
- TimePoint expectedPresentTime,
- Fps frameInterval,
- std::optional<Period> timeoutOpt) {
+status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId,
+ TimePoint expectedPresentTime, Fps frameInterval) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- auto& displayData = mDisplayData[displayId];
- if (!displayData.hwcDisplay) {
- // Display setup has not completed yet
- return BAD_INDEX;
- }
- {
- std::scoped_lock lock{displayData.expectedPresentLock};
- const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp;
- const auto lastFrameInterval = displayData.lastFrameInterval;
- displayData.lastFrameInterval = frameInterval;
- const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2);
-
- const constexpr nsecs_t kOneSecondNs =
- std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
- const bool frameIntervalIsOnCadence =
- isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp,
- lastFrameInterval,
- Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0
- ? timeoutOpt->ns()
- : kOneSecondNs),
- threshold);
-
- const bool expectedPresentWithinTimeout =
- isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
- timeoutOpt, threshold);
-
- using fps_approx_ops::operator!=;
- if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
- }
-
- if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
- return NO_ERROR;
- }
-
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
- }
- ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__,
- expectedPresentTime, frameInterval.getPeriodNsecs());
- const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(),
+ ATRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__,
+ ticks<std::milli, float>(expectedPresentTime - TimePoint::now()),
+ ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs())));
+ const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(),
expectedPresentTime.ns(),
frameInterval.getPeriodNsecs());
-
if (error != hal::Error::NONE) {
ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str());
return INVALID_OPERATION;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index af62731..4ca528a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -303,10 +303,8 @@
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) = 0;
virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
- virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
- TimePoint expectedPresentTime,
- Fps frameInterval,
- std::optional<Period> timeoutOpt) = 0;
+ virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
+ Fps frameInterval) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -466,9 +464,8 @@
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) override;
status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
- status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
- TimePoint expectedPresentTime, Fps frameInterval,
- std::optional<Period> timeoutOpt) override;
+ status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
+ Fps frameInterval) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -494,7 +491,6 @@
private:
// For unit tests
friend TestableSurfaceFlinger;
- friend HWComposerTest;
struct DisplayData {
std::unique_ptr<HWC2::Display> hwcDisplay;
@@ -502,11 +498,6 @@
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
nsecs_t lastPresentTimestamp = 0;
- std::mutex expectedPresentLock;
- TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) =
- TimePoint::fromNs(0);
- Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0);
-
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
bool validateWasSkipped;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 5f1d5f8..c4ff9cc 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -276,8 +276,8 @@
}
}
-bool HidlComposer::getDisplayConfigurationsSupported() const {
- // getDisplayConfigurations is not supported on the HIDL composer.
+bool HidlComposer::isVrrSupported() const {
+ // VRR is not supported on the HIDL composer.
return false;
};
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index c768d27..d78bfb7 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -167,7 +167,7 @@
~HidlComposer() override;
bool isSupported(OptionalFeature) const;
- bool getDisplayConfigurationsSupported() const;
+ bool isVrrSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index dd228b4..a0c943b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -31,10 +31,6 @@
#include <utils/Mutex.h>
#include <utils/Trace.h>
-#include <aidl/android/hardware/power/IPower.h>
-#include <aidl/android/hardware/power/IPowerHintSession.h>
-#include <aidl/android/hardware/power/WorkDuration.h>
-
#include <binder/IServiceManager.h>
#include "../SurfaceFlingerProperties.h"
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 0276e44..bbe51cc0 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -25,9 +25,14 @@
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
+// FMQ library in IPower does questionable conversions
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
-#include <compositionengine/impl/OutputCompositionState.h>
#include <powermanager/PowerHalController.h>
+#pragma clang diagnostic pop
+
+#include <compositionengine/impl/OutputCompositionState.h>
#include <scheduler/Time.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 803299c..d0e2d7a 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -390,6 +390,22 @@
mGpuComposition = true;
}
+// TODO(b/316171339): migrate from perfetto side
+bool SurfaceFrame::isSelfJanky() const {
+ int32_t jankType = getJankType().value_or(JankType::None);
+
+ if (jankType == JankType::None) {
+ return false;
+ }
+
+ int32_t jankBitmask = JankType::AppDeadlineMissed | JankType::Unknown;
+ if (jankType & jankBitmask) {
+ return true;
+ }
+
+ return false;
+}
+
std::optional<int32_t> SurfaceFrame::getJankType() const {
std::scoped_lock lock(mMutex);
if (mPresentState == PresentState::Dropped) {
@@ -1113,20 +1129,23 @@
}
void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousActualPresentTime) const {
+ nsecs_t previousPredictionPresentTime) const {
nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0;
const constexpr float kThresh = 0.5f;
const constexpr float kRange = 1.5f;
for (auto& surfaceFrame : mSurfaceFrames) {
- if (previousActualPresentTime != 0 &&
- static_cast<float>(mSurfaceFlingerActuals.presentTime - previousActualPresentTime) >=
+ if (previousPredictionPresentTime != 0 &&
+ static_cast<float>(mSurfaceFlingerPredictions.presentTime -
+ previousPredictionPresentTime) >=
static_cast<float>(mRenderRate.getPeriodNsecs()) * kRange &&
static_cast<float>(surfaceFrame->getPredictions().presentTime) <=
- (static_cast<float>(mSurfaceFlingerActuals.presentTime) -
+ (static_cast<float>(mSurfaceFlingerPredictions.presentTime) -
kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
static_cast<float>(surfaceFrame->getPredictions().presentTime) >=
- (static_cast<float>(previousActualPresentTime) -
- kThresh * static_cast<float>(mRenderRate.getPeriodNsecs()))) {
+ (static_cast<float>(previousPredictionPresentTime) -
+ kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
+ // sf skipped frame is not considered if app is self janked
+ !surfaceFrame->isSelfJanky()) {
skippedFrameStartTime = surfaceFrame->getPredictions().endTime;
skippedFramePresentTime = surfaceFrame->getPredictions().presentTime;
break;
@@ -1215,18 +1234,18 @@
});
}
-void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousActualPresentTime) const {
+nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousPredictionPresentTime) const {
if (mSurfaceFrames.empty()) {
// We don't want to trace display frames without any surface frames updates as this cannot
// be janky
- return;
+ return previousPredictionPresentTime;
}
if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
// DisplayFrame should not have an invalid token.
ALOGE("Cannot trace DisplayFrame with invalid token");
- return;
+ return previousPredictionPresentTime;
}
if (mPredictionState == PredictionState::Valid) {
@@ -1241,8 +1260,9 @@
}
if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) {
- addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousActualPresentTime);
+ addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime);
}
+ return mSurfaceFlingerPredictions.presentTime;
}
float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
@@ -1333,8 +1353,9 @@
const auto& pendingPresentFence = *mPendingPresentFences.begin();
const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime);
+ displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
+ mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime);
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
@@ -1349,9 +1370,10 @@
}
auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime);
- mPreviousPresentTime = signalTime;
+ displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
+ mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime);
+ mPreviousActualPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
--i;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index b5047a3..a76f7d4 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -165,6 +165,8 @@
TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode);
~SurfaceFrame() = default;
+ bool isSelfJanky() const;
+
// Returns std::nullopt if the frame hasn't been classified yet.
// Used by both SF and FrameTimeline.
std::optional<int32_t> getJankType() const;
@@ -381,8 +383,8 @@
// Emits a packet for perfetto tracing. The function body will be executed only if tracing
// is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME
// and SYSTEM_TIME_MONOTONIC.
- void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousActualPresentTime) const;
+ nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousPredictionPresentTime) const;
// Sets the token, vsyncPeriod, predictions and SF start time.
void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate,
std::optional<TimelineItem> predictions, nsecs_t wakeUpTime);
@@ -508,7 +510,8 @@
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
- nsecs_t mPreviousPresentTime = 0;
+ nsecs_t mPreviousActualPresentTime = 0;
+ nsecs_t mPreviousPredictionPresentTime = 0;
const JankClassificationThresholds mJankClassificationThresholds;
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 1e5a6fb..821ac0c 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -190,8 +190,12 @@
return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
}
-LayerHierarchyBuilder::LayerHierarchyBuilder(
- const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+void LayerHierarchyBuilder::init(const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+ mLayerIdToHierarchy.clear();
+ mHierarchies.clear();
+ mRoot = nullptr;
+ mOffscreenRoot = nullptr;
+
mHierarchies.reserve(layers.size());
mLayerIdToHierarchy.reserve(layers.size());
for (auto& layer : layers) {
@@ -202,6 +206,7 @@
onLayerAdded(layer.get());
}
detachHierarchyFromRelativeParent(&mOffscreenRoot);
+ mInitialized = true;
}
void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
@@ -332,7 +337,7 @@
}
}
-void LayerHierarchyBuilder::update(
+void LayerHierarchyBuilder::doUpdate(
const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
// rebuild map
@@ -381,6 +386,32 @@
attachHierarchyToRelativeParent(&mRoot);
}
+void LayerHierarchyBuilder::update(LayerLifecycleManager& layerLifecycleManager) {
+ if (!mInitialized) {
+ ATRACE_NAME("LayerHierarchyBuilder:init");
+ init(layerLifecycleManager.getLayers());
+ } else if (layerLifecycleManager.getGlobalChanges().test(
+ RequestedLayerState::Changes::Hierarchy)) {
+ ATRACE_NAME("LayerHierarchyBuilder:update");
+ doUpdate(layerLifecycleManager.getLayers(), layerLifecycleManager.getDestroyedLayers());
+ } else {
+ return; // nothing to do
+ }
+
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot);
+ while (hasRelZLoop) {
+ ATRACE_NAME("FixRelZLoop");
+ TransactionTraceWriter::getInstance().invoke("relz_loop_detected",
+ /*overwrite=*/false);
+ layerLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ // reinitialize the hierarchy with the updated layer data
+ init(layerLifecycleManager.getLayers());
+ // check if we have any remaining loops
+ hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot);
+ }
+}
+
const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
return mRoot;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index ba2e262..a1c73c3 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -17,6 +17,7 @@
#pragma once
#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerLifecycleManager.h"
#include "RequestedLayerState.h"
#include "ftl/small_vector.h"
@@ -197,9 +198,8 @@
// hierarchy from a list of RequestedLayerState and associated change flags.
class LayerHierarchyBuilder {
public:
- LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
- void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
- const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
+ LayerHierarchyBuilder() = default;
+ void update(LayerLifecycleManager& layerLifecycleManager);
LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
const LayerHierarchy& getHierarchy() const;
const LayerHierarchy& getOffscreenHierarchy() const;
@@ -213,14 +213,18 @@
void detachFromRelativeParent(LayerHierarchy*);
void attachHierarchyToRelativeParent(LayerHierarchy*);
void detachHierarchyFromRelativeParent(LayerHierarchy*);
-
+ void init(const std::vector<std::unique_ptr<RequestedLayerState>>&);
+ void doUpdate(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
void onLayerDestroyed(RequestedLayerState* layer);
void updateMirrorLayer(RequestedLayerState* layer);
LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
+
std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
LayerHierarchy mRoot{nullptr};
LayerHierarchy mOffscreenRoot{nullptr};
+ bool mInitialized = false;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f7800bb..c8b1059 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2687,6 +2687,7 @@
}
void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId) {
+ if (mFlinger->mLayerLifecycleManagerEnabled) return;
mSnapshot->path.id = clonedFrom->getSequence();
mSnapshot->path.mirrorRootIds.emplace_back(mirrorRootId);
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 693a357..6e862b4 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -100,6 +100,11 @@
case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
return StringPrintf("ModeChanged{displayId=%s, modeId=%u}",
to_string(event.header.displayId).c_str(), event.modeChange.modeId);
+ case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ return StringPrintf("HdcpLevelsChange{displayId=%s, connectedLevel=%d, maxLevel=%d}",
+ to_string(event.header.displayId).c_str(),
+ event.hdcpLevelsChange.connectedLevel,
+ event.hdcpLevelsChange.maxLevel);
default:
return "Event{}";
}
@@ -170,6 +175,20 @@
}};
}
+DisplayEventReceiver::Event makeHdcpLevelsChange(PhysicalDisplayId displayId,
+ int32_t connectedLevel, int32_t maxLevel) {
+ return DisplayEventReceiver::Event{
+ .header =
+ DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ },
+ .hdcpLevelsChange.connectedLevel = connectedLevel,
+ .hdcpLevelsChange.maxLevel = maxLevel,
+ };
+}
+
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
@@ -301,7 +320,7 @@
mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
}
sp<EventThreadConnection> EventThread::createEventConnection(
@@ -442,6 +461,14 @@
mCondition.notify_all();
}
+void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mPendingEvents.push_back(makeHdcpLevelsChange(displayId, connectedLevel, maxLevel));
+ mCondition.notify_all();
+}
+
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
@@ -501,7 +528,7 @@
const auto scheduleResult =
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback");
} else {
mVsyncRegistration.cancel();
@@ -757,7 +784,7 @@
if (reschedule) {
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
}
return oldRegistration;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 7842318..8970103 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -131,6 +131,9 @@
const sp<EventThreadConnection>& connection) const = 0;
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
+
+ virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) = 0;
};
struct IEventThreadCallback {
@@ -177,6 +180,9 @@
void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
+ void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) override;
+
private:
friend EventThreadTest;
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
index cb9bfe9..82af61a 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -15,6 +15,7 @@
*/
#include "FrameRateOverrideMappings.h"
+#include <common/FlagManager.h>
namespace android::scheduler {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -30,7 +31,7 @@
}
}
- {
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
const auto iter = mFrameRateOverridesFromGameManager.find(uid);
if (iter != mFrameRateOverridesFromGameManager.end()) {
return iter->second;
@@ -61,10 +62,13 @@
for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
}
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
- if (std::find_if(overrides.begin(), overrides.end(),
- [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
- overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
+ if (std::find_if(overrides.begin(), overrides.end(),
+ [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
}
}
@@ -93,7 +97,9 @@
if (!hasOverrides) return;
dump(dumper, "setFrameRate"sv, mFrameRateOverridesByContent);
- dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager);
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager);
+ }
dump(dumper, "Backdoor"sv, mFrameRateOverridesFromBackdoor);
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 9c00302..5ce883c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -266,6 +266,7 @@
if (isLayerActive(*info, threshold)) {
// Set layer vote if set
const auto frameRate = info->getSetFrameRateVote();
+
const auto voteType = [&]() {
switch (frameRate.vote.type) {
case Layer::FrameRateCompatibility::Default:
@@ -283,12 +284,40 @@
}
}();
- if (frameRate.isValid()) {
- const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness,
- frameRate.category});
+ if (FlagManager::getInstance().game_default_frame_rate()) {
+ // Determine the layer frame rate considering the following priorities:
+ // 1. Game mode intervention frame rate override
+ // 2. setFrameRate vote
+ // 3. Game default frame rate override
+
+ const auto& [gameModeFrameRateOverride, gameDefaultFrameRateOverride] =
+ getGameFrameRateOverrideLocked(info->getOwnerUid());
+
+ const auto gameFrameRateOverrideVoteType =
+ info->isVisible() ? LayerVoteType::ExplicitDefault : LayerVoteType::NoVote;
+
+ const auto setFrameRateVoteType =
+ info->isVisible() ? voteType : LayerVoteType::NoVote;
+
+ if (gameModeFrameRateOverride.isValid()) {
+ info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
+ } else if (frameRate.isValid()) {
+ info->setLayerVote({setFrameRateVoteType, frameRate.vote.rate,
+ frameRate.vote.seamlessness, frameRate.category});
+ } else if (gameDefaultFrameRateOverride.isValid()) {
+ info->setLayerVote(
+ {gameFrameRateOverrideVoteType, gameDefaultFrameRateOverride});
+ } else {
+ info->resetLayerVote();
+ }
} else {
- info->resetLayerVote();
+ if (frameRate.isValid()) {
+ const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
+ info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness,
+ frameRate.category});
+ } else {
+ info->resetLayerVote();
+ }
}
it++;
@@ -347,4 +376,56 @@
return isSmallDirty;
}
+void LayerHistory::updateGameModeFrameRateOverride(FrameRateOverride frameRateOverride) {
+ const uid_t uid = frameRateOverride.uid;
+ std::lock_guard lock(mLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mGameFrameRateOverride[uid].first = Fps::fromValue(frameRateOverride.frameRateHz);
+ } else {
+ if (mGameFrameRateOverride[uid].second.getValue() == 0.f) {
+ mGameFrameRateOverride.erase(uid);
+ } else {
+ mGameFrameRateOverride[uid].first = Fps();
+ }
+ }
+}
+
+void LayerHistory::updateGameDefaultFrameRateOverride(FrameRateOverride frameRateOverride) {
+ const uid_t uid = frameRateOverride.uid;
+ std::lock_guard lock(mLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mGameFrameRateOverride[uid].second = Fps::fromValue(frameRateOverride.frameRateHz);
+ } else {
+ if (mGameFrameRateOverride[uid].first.getValue() == 0.f) {
+ mGameFrameRateOverride.erase(uid);
+ } else {
+ mGameFrameRateOverride[uid].second = Fps();
+ }
+ }
+}
+
+std::pair<Fps, Fps> LayerHistory::getGameFrameRateOverride(uid_t uid) const {
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ return std::pair<Fps, Fps>();
+ }
+
+ std::lock_guard lock(mLock);
+
+ return getGameFrameRateOverrideLocked(uid);
+}
+
+std::pair<Fps, Fps> LayerHistory::getGameFrameRateOverrideLocked(uid_t uid) const {
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ return std::pair<Fps, Fps>();
+ }
+
+ const auto it = mGameFrameRateOverride.find(uid);
+
+ if (it == mGameFrameRateOverride.end()) {
+ return std::pair<Fps, Fps>(Fps(), Fps());
+ }
+
+ return it->second;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5a9445b..930d06c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -43,6 +43,7 @@
class LayerHistory {
public:
+ using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
using LayerVoteType = RefreshRateSelector::LayerVoteType;
static constexpr std::chrono::nanoseconds kMaxPeriodForHistory = 1s;
@@ -89,6 +90,15 @@
bool isSmallDirtyArea(uint32_t dirtyArea, float threshold) const;
+ // Updates the frame rate override set by game mode intervention
+ void updateGameModeFrameRateOverride(FrameRateOverride frameRateOverride) EXCLUDES(mLock);
+
+ // Updates the frame rate override set by game default frame rate
+ void updateGameDefaultFrameRateOverride(FrameRateOverride frameRateOverride) EXCLUDES(mLock);
+
+ std::pair<Fps, Fps> getGameFrameRateOverride(uid_t uid) const EXCLUDES(mLock);
+ std::pair<Fps, Fps> getGameFrameRateOverrideLocked(uid_t uid) const REQUIRES(mLock);
+
private:
friend class LayerHistoryTest;
friend class LayerHistoryIntegrationTest;
@@ -137,6 +147,13 @@
// Whether a mode change is in progress or not
std::atomic<bool> mModeChangePending = false;
+
+ // A list to look up the game frame rate overrides
+ // Each entry includes:
+ // 1. the uid of the app
+ // 2. a pair of game mode intervention frame frame and game default frame rate override
+ // set to 0.0 if there is no such override
+ std::map<uid_t, std::pair<Fps, Fps>> mGameFrameRateOverride GUARDED_BY(mLock);
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 50bb83d..326e444 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -175,7 +175,8 @@
bool pendingModeChange, const LayerProps& props);
// Sets an explicit layer vote. This usually comes directly from the application via
- // ANativeWindow_setFrameRate API
+ // ANativeWindow_setFrameRate API. This is also used by Game Default Frame Rate and
+ // Game Mode Intervention Frame Rate.
void setLayerVote(LayerVote vote) { mLayerVote = vote; }
// Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 18c0a69..cf8b3bf 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -125,7 +125,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
return oldRegistration;
}
@@ -143,7 +143,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
void MessageQueue::waitMessage() {
@@ -196,7 +196,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index aa8d54d..bfc47e6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -412,6 +412,17 @@
thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
}
+void Scheduler::onHdcpLevelsChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ int32_t connectedLevel, int32_t maxLevel) {
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
+}
+
void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
{
std::lock_guard<std::mutex> lock(mPolicyLock);
@@ -600,8 +611,10 @@
const Display& display = *displayOpt;
const nsecs_t threshold =
display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2;
- const nsecs_t nextVsyncTime = display.schedulePtr->getTracker().nextAnticipatedVSyncTimeFrom(
- currentExpectedPresentTime.ns() + threshold);
+ const nsecs_t nextVsyncTime =
+ display.schedulePtr->getTracker()
+ .nextAnticipatedVSyncTimeFrom(currentExpectedPresentTime.ns() + threshold,
+ currentExpectedPresentTime.ns());
return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns());
}
@@ -630,6 +643,7 @@
}
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
+ ATRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str());
const auto scheduleOpt =
(ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) {
return display.powerMode == hal::PowerMode::OFF
@@ -640,7 +654,8 @@
if (!scheduleOpt) return;
const auto& schedule = scheduleOpt->get();
- if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) {
+ const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence));
+ if (needMoreSignals) {
schedule->enableHardwareVsync();
} else {
constexpr bool kDisallow = false;
@@ -1226,12 +1241,27 @@
mLayerHistory.setDisplayArea(displayArea);
}
-void Scheduler::setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) {
+void Scheduler::setGameModeFrameRateForUid(FrameRateOverride frameRateOverride) {
if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
return;
}
- mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
+ if (FlagManager::getInstance().game_default_frame_rate()) {
+ // update the frame rate override mapping in LayerHistory
+ mLayerHistory.updateGameModeFrameRateOverride(frameRateOverride);
+ } else {
+ mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
+ }
+}
+
+void Scheduler::setGameDefaultFrameRateForUid(FrameRateOverride frameRateOverride) {
+ if (!FlagManager::getInstance().game_default_frame_rate() ||
+ (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f)) {
+ return;
+ }
+
+ // update the frame rate override mapping in LayerHistory
+ mLayerHistory.updateGameDefaultFrameRateOverride(frameRateOverride);
}
void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 454ef83..a29d153 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -173,6 +173,8 @@
void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
EXCLUDES(mConnectionsLock);
+ void onHdcpLevelsChanged(ConnectionHandle, PhysicalDisplayId, int32_t, int32_t);
+
// Modifies work duration in the event thread.
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
@@ -293,7 +295,17 @@
// FrameRateOverride.refreshRateHz == 0 means no preference.
void setPreferredRefreshRateForUid(FrameRateOverride);
- void setGameModeRefreshRateForUid(FrameRateOverride);
+ // Stores the frame rate override that a game should run at set by game interventions.
+ // FrameRateOverride.refreshRateHz == 0 means no preference.
+ void setGameModeFrameRateForUid(FrameRateOverride) EXCLUDES(mDisplayLock);
+
+ // Stores the frame rate override that a game should run rat set by default game frame rate.
+ // FrameRateOverride.refreshRateHz == 0 means no preference, game default game frame rate is not
+ // enabled.
+ //
+ // "ro.surface_flinger.game_default_frame_rate_override" sets the frame rate value,
+ // "persist.graphics.game_default_frame_rate.enabled" controls whether this feature is enabled.
+ void setGameDefaultFrameRateForUid(FrameRateOverride) EXCLUDES(mDisplayLock);
void updateSmallAreaDetection(std::vector<std::pair<int32_t, float>>& uidThresholdMappings);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index c3a952f..f978016 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -84,8 +84,8 @@
* able to provide the ready-by time (deadline) on the callback.
* For internal clients, we don't need to add additional padding, so
* readyDuration will typically be 0.
- * @earliestVsync: The targeted display time. This will be snapped to the closest
- * predicted vsync time after earliestVsync.
+ * @lastVsync: The targeted display time. This will be snapped to the closest
+ * predicted vsync time after lastVsync.
*
* callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
@@ -93,11 +93,11 @@
struct ScheduleTiming {
nsecs_t workDuration = 0;
nsecs_t readyDuration = 0;
- nsecs_t earliestVsync = 0;
+ nsecs_t lastVsync = 0;
bool operator==(const ScheduleTiming& other) const {
return workDuration == other.workDuration && readyDuration == other.readyDuration &&
- earliestVsync == other.earliestVsync;
+ lastVsync == other.lastVsync;
}
bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
@@ -109,12 +109,12 @@
* The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
*
- * The caller designates the earliest vsync event that should be targeted by the earliestVsync
+ * The caller designates the earliest vsync event that should be targeted by the lastVsync
* parameter.
* The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where
- * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ).
+ * predictedVsync is the first vsync event time where ( predictedVsync >= lastVsync ).
*
- * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has
+ * If (workDuration + readyDuration - lastVsync) is in the past, or if a callback has
* already been dispatched for the predictedVsync, an error will be returned.
*
* It is valid to reschedule a callback to a different time.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ef30887..5cb0ffb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -45,8 +45,11 @@
nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
const VSyncDispatch::ScheduleTiming& timing) {
- const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
- std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+ const auto nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
+ now + timing.workDuration +
+ timing.readyDuration),
+ timing.lastVsync);
return getExpectedCallbackTime(nextVsyncTime, timing);
}
@@ -93,8 +96,11 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
- auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
- std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+ auto nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
+ now + timing.workDuration +
+ timing.readyDuration),
+ timing.lastVsync);
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
bool const wouldSkipAVsyncTarget =
@@ -139,11 +145,13 @@
bool const nextVsyncTooClose = mLastDispatchTime &&
(nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
if (alreadyDispatchedForVsync) {
- return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
+ return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
+ *mLastDispatchTime);
}
if (nextVsyncTooClose) {
- return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod);
+ return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
+ *mLastDispatchTime + currentPeriod);
}
return nextVsyncTime;
@@ -160,11 +168,12 @@
}
const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
- const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
+ const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.lastVsync);
const auto nextVsyncTime =
adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/
- tracker.nextAnticipatedVSyncTimeFrom(earliestVsync));
+ tracker.nextAnticipatedVSyncTimeFrom(earliestVsync,
+ mScheduleTiming.lastVsync));
const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
@@ -214,10 +223,10 @@
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
StringAppendF(&result,
- "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
+ "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative "
"to now\n",
mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
- (mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
+ (mScheduleTiming.lastVsync - systemTime()) / 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
@@ -237,6 +246,7 @@
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
std::lock_guard lock(mMutex);
+ mRunning = false;
cancelTimer();
for (auto& [_, entry] : mCallbacks) {
ALOGE("Forgot to unregister a callback on VSyncDispatch!");
@@ -305,6 +315,10 @@
std::vector<Invocation> invocations;
{
std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGD("TimerQueue is not running. Skipping callback.");
+ return;
+ }
auto const now = mTimeKeeper->now();
mLastTimerCallback = now;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index e0fb8f9..3d08410 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -148,6 +148,10 @@
std::mutex mutable mMutex;
+ // During VSyncDispatchTimerQueue deconstruction, skip timerCallback to
+ // avoid crash
+ bool mRunning = true;
+
static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
std::unique_ptr<TimeKeeper> const mTimeKeeper;
VsyncSchedule::TrackerPtr mTracker;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 7379a46..33d3652 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -88,6 +88,7 @@
(timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent &&
percent <= (kMaxPercent - kOutlierTolerancePercent)) {
+ ATRACE_FORMAT_INSTANT("timestamp is not aligned with model");
return false;
}
@@ -98,6 +99,7 @@
const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod();
if (distancePercent < kOutlierTolerancePercent) {
// duplicate timestamp
+ ATRACE_FORMAT_INSTANT("duplicate timestamp");
return false;
}
return true;
@@ -126,6 +128,8 @@
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+ ATRACE_CALL();
+
std::lock_guard lock(mMutex);
if (!validate(timestamp)) {
@@ -144,6 +148,8 @@
} else {
mKnownTimestamp = timestamp;
}
+ ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
+ (systemTime() - *mKnownTimestamp) / 1e6f);
return false;
}
@@ -247,7 +253,7 @@
}
auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
- const auto vsync = nextAnticipatedVSyncTimeFromLocked(timestamp);
+ const auto vsync = snapToVsync(timestamp);
if (!mLastVsyncSequence) return {vsync, 0};
const auto [slope, _] = getVSyncPredictionModelLocked();
@@ -257,7 +263,7 @@
return {vsync, vsyncSequence};
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
auto const [slope, intercept] = getVSyncPredictionModelLocked();
if (mTimestamps.empty()) {
@@ -293,9 +299,29 @@
return prediction;
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
+ std::optional<nsecs_t> lastVsyncOpt) const {
+ ATRACE_CALL();
std::lock_guard lock(mMutex);
+ const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
+ const auto threshold = currentPeriod / 2;
+ const auto minFramePeriod = minFramePeriodLocked().ns();
+ const auto lastFrameMissed =
+ lastVsyncOpt && std::abs(*lastVsyncOpt - mLastMissedVsync.ns()) < threshold;
+ const nsecs_t baseTime =
+ FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt
+ ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold)
+ : timePoint;
+ const auto vsyncTime = snapToVsyncAlignedWithRenderRate(baseTime);
+ if (FlagManager::getInstance().vrr_config()) {
+ const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
+ const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
+ mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate);
+ }
+ return vsyncTime;
+}
+nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const {
// update the mLastVsyncSequence for reference point
mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
@@ -319,30 +345,12 @@
}();
if (renderRatePhase == 0) {
- const auto vsyncTime = mLastVsyncSequence->vsyncTime;
- if (FlagManager::getInstance().vrr_config()) {
- const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
- ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__,
- ticks<std::milli, float>(vsyncTimePoint - TimePoint::now()));
- const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
- mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate);
- }
- return vsyncTime;
+ return mLastVsyncSequence->vsyncTime;
}
auto const [slope, intercept] = getVSyncPredictionModelLocked();
const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
- const auto nextAnticipatedVsyncTime =
- nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
- if (FlagManager::getInstance().vrr_config()) {
- const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime);
- ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__,
- ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now()));
- const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
- mVsyncTrackerCallback.onVsyncGenerated(nextAnticipatedVsyncTimePoint, mDisplayModePtr,
- renderRate);
- }
- return nextAnticipatedVsyncTime;
+ return snapToVsync(approximateNextVsync - slope / 2);
}
/*
@@ -445,6 +453,7 @@
if (mLastVsyncSequence) {
mLastVsyncSequence->vsyncTime += phase.ns();
}
+ mPastExpectedPresentTimes.clear();
}
}
}
@@ -462,23 +471,17 @@
lastConfirmedPresentTime.ns()) /
1e6f);
}
- mPastExpectedPresentTimes.push_back(expectedPresentTime);
-
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
+ mPastExpectedPresentTimes.push_back(expectedPresentTime);
- const auto minFramePeriod = minFramePeriodLocked().ns();
while (!mPastExpectedPresentTimes.empty()) {
const auto front = mPastExpectedPresentTimes.front().ns();
- const bool frontIsLastConfirmed =
- std::abs(front - lastConfirmedPresentTime.ns()) < threshold;
- const bool frontIsBeforeConfirmed =
- front < lastConfirmedPresentTime.ns() - minFramePeriod + threshold;
- if (frontIsLastConfirmed || frontIsBeforeConfirmed) {
+ const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
+ if (frontIsBeforeConfirmed) {
if (CC_UNLIKELY(mTraceOn)) {
ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
- static_cast<float>(lastConfirmedPresentTime.ns() -
- mPastExpectedPresentTimes.front().ns()) /
+ static_cast<float>(lastConfirmedPresentTime.ns() - front) /
1e6f);
}
mPastExpectedPresentTimes.pop_front();
@@ -502,6 +505,7 @@
TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);
ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+ mLastMissedVsync = expectedPresentTime;
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
@@ -515,6 +519,8 @@
}
void VSyncPredictor::clearTimestamps() {
+ ATRACE_CALL();
+
if (!mTimestamps.empty()) {
auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
if (mKnownTimestamp) {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 72a3431..9191003 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -45,7 +45,9 @@
~VSyncPredictor();
bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
+ std::optional<nsecs_t> lastVsyncOpt = {}) const final
+ EXCLUDES(mMutex);
nsecs_t currentPeriod() const final EXCLUDES(mMutex);
Period minFramePeriod() const final EXCLUDES(mMutex);
void resetModel() final EXCLUDES(mMutex);
@@ -87,7 +89,8 @@
size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
- nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
+ nsecs_t snapToVsync(nsecs_t timePoint) const REQUIRES(mMutex);
+ nsecs_t snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const REQUIRES(mMutex);
bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
Period minFramePeriodLocked() const REQUIRES(mMutex);
void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
@@ -120,6 +123,8 @@
mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
+
+ TimePoint mLastMissedVsync GUARDED_BY(mMutex);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 24737e4..186a2d6 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -53,6 +53,8 @@
VSyncReactor::~VSyncReactor() = default;
bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) {
+ ATRACE_CALL();
+
if (!fence) {
return false;
}
@@ -64,6 +66,8 @@
std::lock_guard lock(mMutex);
if (mExternalIgnoreFences || mInternalIgnoreFences) {
+ ATRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d",
+ mExternalIgnoreFences, mInternalIgnoreFences);
return true;
}
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 1ed863c..417163f 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -57,9 +57,13 @@
* is updated.
*
* \param [in] timePoint The point in time after which to estimate a vsync event.
+ * \param [in] lastVsyncOpt The last vsync time used by the client. If provided, the tracker
+ * should use that as a reference point when generating the new vsync
+ * and avoid crossing the minimal frame period of a VRR display.
* \return A prediction of the timestamp of a vsync event.
*/
- virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+ virtual nsecs_t nextAnticipatedVSyncTimeFrom(
+ nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt = {}) const = 0;
/*
* The current period of the vsync signal.
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index ff1c9e9..db6a187 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -19,6 +19,7 @@
#include <common/FlagManager.h>
#include <ftl/fake_guard.h>
+#include <gui/TraceUtils.h>
#include <scheduler/Fps.h>
#include <scheduler/Timer.h>
@@ -179,6 +180,7 @@
}
void VsyncSchedule::enableHardwareVsyncLocked() {
+ ATRACE_CALL();
if (mHwVsyncState == HwVsyncState::Disabled) {
getTracker().resetModel();
mRequestHardwareVsync(mId, true);
@@ -187,6 +189,7 @@
}
void VsyncSchedule::disableHardwareVsync(bool disallow) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> lock(mHwVsyncLock);
switch (mHwVsyncState) {
case HwVsyncState::Enabled:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 772fcd1..138a9bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -59,6 +59,7 @@
#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
+#include <ftl/match.h>
#include <ftl/unit.h>
#include <gui/AidlStatusUtil.h>
#include <gui/BufferQueue.h>
@@ -318,6 +319,53 @@
return duration > (Clock::now() - updateTime);
}
+bool isFrameIntervalOnCadence(TimePoint expectedPresentTime, TimePoint lastExpectedPresentTimestamp,
+ Fps lastFrameInterval, Period timeout, Duration threshold) {
+ if (lastFrameInterval.getPeriodNsecs() == 0) {
+ return false;
+ }
+
+ const auto expectedPresentTimeDeltaNs =
+ expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns();
+
+ if (expectedPresentTimeDeltaNs > timeout.ns()) {
+ return false;
+ }
+
+ const auto expectedPresentPeriods = static_cast<nsecs_t>(
+ std::round(static_cast<float>(expectedPresentTimeDeltaNs) /
+ static_cast<float>(lastFrameInterval.getPeriodNsecs())));
+ const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods;
+ const auto calculatedExpectedPresentTimeNs =
+ lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs;
+ const auto presentTimeDelta =
+ std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs);
+ return presentTimeDelta < threshold.ns();
+}
+
+bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime,
+ TimePoint lastExpectedPresentTimestamp,
+ std::optional<Period> timeoutOpt, Duration threshold) {
+ if (!timeoutOpt) {
+ // Always within timeout if timeoutOpt is absent and don't send hint
+ // for the timeout
+ return true;
+ }
+
+ if (timeoutOpt->ns() == 0) {
+ // Always outside timeout if timeoutOpt is 0 and always send
+ // the hint for the timeout.
+ return false;
+ }
+
+ if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) {
+ return true;
+ }
+
+ // Check if within the threshold as it can be just outside the timeout
+ return std::abs(expectedPresentTime.ns() -
+ (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns();
+}
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -504,6 +552,12 @@
base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true);
mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
+
+ // These are set by the HWC implementation to indicate that they will use the workarounds.
+ mIsHotplugErrViaNegVsync =
+ base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false);
+
+ mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -716,7 +770,7 @@
sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
@@ -1182,20 +1236,21 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) {
- const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
+void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) {
+ const auto mode = desiredMode.mode;
+ const auto displayId = mode.modePtr->getPhysicalDisplayId();
+
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
- ALOGW("%s: display is no longer valid", __func__);
+ ALOGW("%s: Unknown display %s", __func__, to_string(displayId).c_str());
return;
}
- const auto mode = request.mode;
- const bool emitEvent = request.emitEvent;
+ const bool emitEvent = desiredMode.emitEvent;
- switch (display->setDesiredMode(std::move(request), force)) {
+ switch (display->setDesiredMode(std::move(desiredMode), force)) {
case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch:
// DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler.
mScheduler->setRenderRate(displayId,
@@ -1245,7 +1300,7 @@
}
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
const auto displayOpt =
FTL_FAKE_GUARD(mStateLock,
ftl::find_if(mPhysicalDisplays,
@@ -1291,61 +1346,55 @@
const auto displayId = display.getPhysicalId();
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto pendingModeOpt = display.getPendingMode();
- if (!pendingModeOpt) {
- // There is no pending mode change. This can happen if the active
- // display changed and the mode change happened on a different display.
- return;
- }
+ ftl::match(
+ display.finalizeModeChange(),
+ [this, displayId](DisplayDevice::RefreshRateChange change) {
+ ftl::FakeGuard guard(mStateLock);
- const auto& activeMode = pendingModeOpt->mode;
+ if (change.activeMode.emitEvent) {
+ dispatchDisplayModeChangeEvent(displayId, change.activeMode.mode);
+ }
- if (display.getActiveMode().modePtr->getResolution() != activeMode.modePtr->getResolution()) {
- auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
- // We need to generate new sequenceId in order to recreate the display (and this
- // way the framebuffer).
- state.sequenceId = DisplayDeviceState{}.sequenceId;
- state.physical->activeMode = activeMode.modePtr.get();
- processDisplayChangesLocked();
+ applyActiveMode(std::move(change.activeMode));
+ },
+ [&](DisplayDevice::ResolutionChange change) {
+ auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
+ // Assign a new sequence ID to recreate the display and so its framebuffer.
+ state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.physical->activeMode = change.activeMode.mode.modePtr.get();
- // processDisplayChangesLocked will update all necessary components so we're done here.
- return;
- }
+ ftl::FakeGuard guard1(kMainThreadContext);
+ ftl::FakeGuard guard2(mStateLock);
+ processDisplayChangesLocked();
- display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
- activeMode.fps);
-
- if (displayId == mActiveDisplayId) {
- mRefreshRateStats->setRefreshRate(activeMode.fps);
- updatePhaseConfiguration(activeMode.fps);
- }
-
- if (pendingModeOpt->emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, activeMode);
- }
+ applyActiveMode(std::move(change.activeMode));
+ },
+ [](DisplayDevice::NoModeChange noChange) {
+ // TODO(b/255635821): Remove this case, as it should no longer happen.
+ ALOGE("A mode change was initiated but not finalized: %s", noChange.reason);
+ });
}
-void SurfaceFlinger::dropModeRequest(const sp<DisplayDevice>& display) {
- display->clearDesiredMode();
- if (display->getPhysicalId() == mActiveDisplayId) {
+void SurfaceFlinger::dropModeRequest(display::DisplayModeRequest&& request) {
+ if (request.mode.modePtr->getPhysicalDisplayId() == mActiveDisplayId) {
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
-void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) {
- const auto activeModeOpt = display->getDesiredMode();
- auto activeModePtr = activeModeOpt->mode.modePtr;
+void SurfaceFlinger::applyActiveMode(display::DisplayModeRequest&& activeMode) {
+ auto activeModePtr = activeMode.mode.modePtr;
const auto displayId = activeModePtr->getPhysicalDisplayId();
- const auto renderFps = activeModeOpt->mode.fps;
+ const auto renderFps = activeMode.mode.fps;
- dropModeRequest(display);
+ dropModeRequest(std::move(activeMode));
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
mScheduler->setRenderRate(displayId, renderFps);
if (displayId == mActiveDisplayId) {
+ mRefreshRateStats->setRefreshRate(renderFps);
updatePhaseConfiguration(renderFps);
}
}
@@ -1359,50 +1408,50 @@
const auto display = getDisplayDeviceLocked(id);
if (!display) continue;
- auto desiredModeOpt = display->getDesiredMode();
+ auto desiredModeOpt = display->takeDesiredMode();
if (!desiredModeOpt) {
continue;
}
+ auto desiredMode = std::move(*desiredModeOpt);
+
if (!shouldApplyRefreshRateSelectorPolicy(*display)) {
- dropModeRequest(display);
+ dropModeRequest(std::move(desiredMode));
continue;
}
- const auto desiredModeId = desiredModeOpt->mode.modePtr->getId();
+ const auto desiredModeId = desiredMode.mode.modePtr->getId();
const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId);
if (!displayModePtrOpt) {
- ALOGW("Desired display mode is no longer supported. Mode ID = %d",
- desiredModeId.value());
- dropModeRequest(display);
+ ALOGW("%s: Unknown mode %d for display %s", __func__, desiredModeId.value(),
+ to_string(id).c_str());
+ dropModeRequest(std::move(desiredMode));
continue;
}
- ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
- to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
- to_string(display->getId()).c_str());
-
- if (display->getActiveMode() == desiredModeOpt->mode) {
- applyActiveMode(display);
+ if (display->getActiveMode() == desiredMode.mode) {
+ dropModeRequest(std::move(desiredMode));
continue;
}
- // Desired active mode was set, it is different than the mode currently in use, however
- // allowed modes might have changed by the time we process the refresh.
- // Make sure the desired mode is still allowed
- if (!display->refreshRateSelector().isModeAllowed(desiredModeOpt->mode)) {
- dropModeRequest(display);
+ // The desired mode is different from the active mode. However, the allowed modes might have
+ // changed since setDesiredMode scheduled a mode transition.
+ if (!display->refreshRateSelector().isModeAllowed(desiredMode.mode)) {
+ dropModeRequest(std::move(desiredMode));
continue;
}
- // TODO(b/142753666) use constrains
+ ALOGV("Mode setting display %s to %d (%s)", to_string(id).c_str(), desiredModeId.value(),
+ to_string(displayModePtrOpt.value().get()->getVsyncRate()).c_str());
+
+ // TODO(b/142753666): Use constraints.
hal::VsyncPeriodChangeConstraints constraints;
constraints.desiredTimeNanos = systemTime();
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (!display->initiateModeChange(std::move(*desiredModeOpt), constraints, outTimeline)) {
+ if (!display->initiateModeChange(std::move(desiredMode), constraints, outTimeline)) {
continue;
}
@@ -1422,17 +1471,12 @@
if (displayToUpdateImmediately) {
const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
finalizeDisplayModeChange(*display);
-
- const auto desiredModeOpt = display->getDesiredMode();
- if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->mode) {
- applyActiveMode(display);
- }
}
}
void SurfaceFlinger::disableExpensiveRendering() {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
ATRACE_NAME(whence);
if (mPowerAdvisor->isUsingExpensiveRendering()) {
for (const auto& [_, display] : mDisplays) {
@@ -1474,7 +1518,7 @@
}
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto displayOpt =
ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
@@ -1555,7 +1599,7 @@
status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
DisplayModeId modeId) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto snapshotOpt =
ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
@@ -1583,7 +1627,7 @@
status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().clearBootDisplayMode(*displayId);
} else {
@@ -1622,7 +1666,7 @@
ALOGE("hdrOutputConversion is not supported by this device.");
return INVALID_OPERATION;
}
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t {
using AidlHdrConversionStrategy =
aidl::android::hardware::graphics::common::HdrConversionStrategy;
using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
@@ -1682,7 +1726,7 @@
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
getHwComposer().setAutoLowLatencyMode(*displayId, on);
} else {
@@ -1693,7 +1737,7 @@
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
getHwComposer().setContentType(*displayId, type);
@@ -1747,7 +1791,7 @@
bool enable, uint8_t componentMask,
uint64_t maxFrames) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
componentMask, maxFrames);
@@ -1800,7 +1844,7 @@
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
outLayers->clear();
- auto future = mScheduler->schedule([=] {
+ auto future = mScheduler->schedule([=, this] {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
mDrawingState.traverseInZOrder([&](Layer* layer) {
outLayers->push_back(layer->getLayerDebugInfo(display.get()));
@@ -1906,7 +1950,7 @@
}
const char* const whence = __func__;
- return ftl::Future(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto display = getDisplayDeviceLocked(displayToken)) {
const bool supportsDisplayBrightnessCommand =
getHwComposer().getComposer()->isSupported(
@@ -2083,15 +2127,28 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- if (FlagManager::getInstance().connected_display()) {
+ if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
+ vsyncPeriod.has_value()) {
// use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
- if (mIsHotplugErrViaNegVsync && timestamp < 0 && vsyncPeriod.has_value() &&
- vsyncPeriod.value() == ~0) {
- int hotplugErrorCode = static_cast<int32_t>(-timestamp);
- ALOGD("SurfaceFlinger got hotplugErrorCode=%d", hotplugErrorCode);
+ if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) {
+ const int32_t hotplugErrorCode = static_cast<int32_t>(-timestamp);
+ ALOGD("SurfaceFlinger got hotplugErrorCode=%d for display %" PRIu64, hotplugErrorCode,
+ hwcDisplayId);
mScheduler->onHotplugConnectionError(mAppConnectionHandle, hotplugErrorCode);
return;
}
+
+ if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
+ const int32_t value = static_cast<int32_t>(-timestamp);
+ // one byte is good enough to encode android.hardware.drm.HdcpLevel
+ const int32_t maxLevel = (value >> 8) & 0xFF;
+ const int32_t connectedLevel = value & 0xFF;
+ ALOGD("SurfaceFlinger got HDCP level changed: connected=%d, max=%d for "
+ "display=%" PRIu64,
+ connectedLevel, maxLevel, hwcDisplayId);
+ updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
+ return;
+ }
}
ATRACE_NAME(vsyncPeriod
@@ -2160,19 +2217,16 @@
void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
ATRACE_CALL();
if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) {
- const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos);
- ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue());
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
- {
- {
- const auto display = getDisplayDeviceLocked(*displayId);
- FTL_FAKE_GUARD(kMainThreadContext,
- display->updateRefreshRateOverlayRate(fps,
- display->getActiveMode()
- .fps,
- /* setByHwc */ true));
- }
- }
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
+ const Fps fps = Fps::fromPeriodNsecs(getHwComposer().getComposer()->isVrrSupported()
+ ? data.refreshPeriodNanos
+ : data.vsyncPeriodNanos);
+ ATRACE_FORMAT("%s Fps %d", whence, fps.getIntValue());
+ const auto display = getDisplayDeviceLocked(*displayId);
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->updateRefreshRateOverlayRate(fps, display->getActiveMode().fps,
+ /* setByHwc */ true));
}));
}
}
@@ -2237,8 +2291,9 @@
}
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldn't find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldn't find layer object for %s",
+ snapshot->getDebugString().c_str());
if (updateSmallDirty) {
// Update small dirty flag while surface damage region or geometry changed
@@ -2327,11 +2382,7 @@
mLegacyLayers[layer->sequence] = layer;
}
}
- if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
- ATRACE_NAME("LayerHierarchyBuilder:update");
- mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
- mLayerLifecycleManager.getDestroyedLayers());
- }
+ mLayerHierarchyBuilder.update(mLayerLifecycleManager);
}
bool mustComposite = false;
@@ -2391,8 +2442,9 @@
const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
auto it = mLegacyLayers.find(layer->id);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- layer->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ layer->getDebugString().c_str());
if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) {
if (!it->second->hasBuffer()) {
// The last latch time is used to classify a missed frame as buffer stuffing
@@ -2708,7 +2760,18 @@
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
refreshArgs.expectedPresentTime = expectedPresentTime.ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
-
+ {
+ auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId];
+ auto lastExpectedPresentTimestamp = TimePoint::fromNs(
+ notifyExpectedPresentData.lastExpectedPresentTimestamp.load().ns());
+ if (expectedPresentTime > lastExpectedPresentTimestamp) {
+ // If the values are not same, then hint is sent with newer value.
+ // And because composition always follows the notifyExpectedPresentIfRequired, we can
+ // skip updating the lastExpectedPresentTimestamp in this case.
+ notifyExpectedPresentData.lastExpectedPresentTimestamp
+ .compare_exchange_weak(lastExpectedPresentTimestamp, expectedPresentTime);
+ }
+ }
// Store the present time just before calling to the composition engine so we could notify
// the scheduler.
const auto presentTime = systemTime();
@@ -3091,9 +3154,9 @@
[&, compositionDisplay = compositionDisplay](
std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
auto& legacyLayer = it->second;
sp<LayerFE> layerFe =
legacyLayer->getCompositionEngineLayerFE(snapshot->path);
@@ -3375,9 +3438,10 @@
}
const sp<IBinder> token = sp<BBinder>::make();
+ const ui::DisplayConnectionType connectionType =
+ getHwComposer().getDisplayConnectionType(displayId);
- mPhysicalDisplays.try_emplace(displayId, token, displayId,
- getHwComposer().getDisplayConnectionType(displayId),
+ mPhysicalDisplays.try_emplace(displayId, token, displayId, connectionType,
std::move(displayModes), std::move(colorModes),
std::move(info.deviceProductInfo));
@@ -3385,7 +3449,7 @@
state.physical = {.id = displayId,
.hwcDisplayId = hwcDisplayId,
.activeMode = std::move(activeMode)};
- state.isSecure = true; // All physical displays are currently considered secure.
+ state.isSecure = connectionType == ui::DisplayConnectionType::Internal;
state.isProtected = true;
state.displayName = std::move(info.name);
@@ -4062,7 +4126,7 @@
void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime,
ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) {
const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod();
- const auto timeout = [&]() -> std::optional<Period> {
+ const auto timeoutOpt = [&]() -> std::optional<Period> {
const auto vrrConfig = modePtr->getVrrConfig();
if (!vrrConfig) return std::nullopt;
@@ -4072,14 +4136,54 @@
return Period::fromNs(notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs);
}();
- const auto displayId = modePtr->getPhysicalDisplayId();
- const auto status = getHwComposer().notifyExpectedPresentIfRequired(displayId, vsyncPeriod,
- expectedPresentTime,
- renderRate, timeout);
- if (status != NO_ERROR) {
- ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__,
- displayId.value);
+ notifyExpectedPresentIfRequired(modePtr->getPhysicalDisplayId(), vsyncPeriod,
+ expectedPresentTime, renderRate, timeoutOpt);
+}
+
+void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
+ Period vsyncPeriod,
+ TimePoint expectedPresentTime,
+ Fps frameInterval,
+ std::optional<Period> timeoutOpt) {
+ {
+ auto& data = mNotifyExpectedPresentMap[displayId];
+ const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp.load();
+ const auto lastFrameInterval = data.lastFrameInterval;
+ data.lastFrameInterval = frameInterval;
+ const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2);
+
+ const constexpr nsecs_t kOneSecondNs =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ const auto timeout = Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns()
+ : kOneSecondNs);
+ const bool frameIntervalIsOnCadence =
+ isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp,
+ lastFrameInterval, timeout, threshold);
+
+ const bool expectedPresentWithinTimeout =
+ isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
+ timeoutOpt, threshold);
+
+ using fps_approx_ops::operator!=;
+ if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
+ data.lastExpectedPresentTimestamp = expectedPresentTime;
+ }
+
+ if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ return;
+ }
+ data.lastExpectedPresentTimestamp = expectedPresentTime;
}
+
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
+ const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime,
+ frameInterval);
+ if (status != NO_ERROR) {
+ ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence,
+ displayId.value);
+ }
+ }));
}
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
@@ -4151,9 +4255,6 @@
sp<RegionSamplingThread>::make(*this,
RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
-
- mIsHotplugErrViaNegVsync =
- base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false);
}
void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) {
@@ -4341,7 +4442,7 @@
if (mNumLayers >= MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
MAX_LAYERS);
- static_cast<void>(mScheduler->schedule([=] {
+ static_cast<void>(mScheduler->schedule([=, this] {
ALOGE("Dumping layer keeping > 20 children alive:");
bool leakingParentLayerFound = false;
mDrawingState.traverse([&](Layer* layer) {
@@ -5925,7 +6026,7 @@
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
kMainThreadContext) {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
@@ -5952,140 +6053,60 @@
!PermissionCache::checkPermission(sDump, pid, uid)) {
StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
pid, uid);
- } else {
- Dumper hwclayersDump = [this](const DumpArgs&, bool, std::string& result)
- FTL_FAKE_GUARD(mStateLock) -> void const {
- if (mLayerLifecycleManagerEnabled) {
- mScheduler
- ->schedule([this, &result]() FTL_FAKE_GUARD(kMainThreadContext)
- FTL_FAKE_GUARD(mStateLock) {
- dumpHwcLayersMinidump(result);
- })
- .get();
- } else {
- dumpHwcLayersMinidumpLockedLegacy(result);
- }
- };
-
- static const std::unordered_map<std::string, Dumper> dumpers = {
- {"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
- {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
- {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
- {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
- {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
- {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
- {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
- {"--hwclayers"s, std::move(hwclayersDump)},
- {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
- {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
- {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
- {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
- {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
- {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
- {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)},
- {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
- {"--frontend"s, dumper(&SurfaceFlinger::dumpFrontEnd)},
- };
-
- const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
-
- // Traversal of drawing state must happen on the main thread.
- // Otherwise, SortedVector may have shared ownership during concurrent
- // traversals, which can result in use-after-frees.
- std::string compositionLayers;
- mScheduler
- ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
- if (!mLayerLifecycleManagerEnabled) {
- StringAppendF(&compositionLayers, "Composition layers\n");
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- auto* compositionState = layer->getCompositionState();
- if (!compositionState || !compositionState->isVisible) return;
- android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n",
- layer,
- layer->getDebugName()
- ? layer->getDebugName()
- : "<unknown>");
- compositionState->dump(compositionLayers);
- });
- } else {
- std::ostringstream out;
- out << "\nComposition list\n";
- ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
- if (snapshot->hasSomethingToDraw()) {
- if (lastPrintedLayerStackHeader !=
- snapshot->outputFilter.layerStack) {
- lastPrintedLayerStackHeader =
- snapshot->outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id
- << "\n";
- }
- out << " " << *snapshot << "\n";
- }
- });
-
- out << "\nInput list\n";
- lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachInputSnapshot(
- [&](const frontend::LayerSnapshot& snapshot) {
- if (lastPrintedLayerStackHeader !=
- snapshot.outputFilter.layerStack) {
- lastPrintedLayerStackHeader =
- snapshot.outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id
- << "\n";
- }
- out << " " << snapshot << "\n";
- });
-
- out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy()
- << "\nOffscreen Hierarchy\n"
- << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
- compositionLayers = out.str();
- dumpHwcLayersMinidump(compositionLayers);
- }
- })
- .get();
-
- bool dumpLayers = true;
- {
- TimedLock lock(mStateLock, s2ns(1), __func__);
- if (!lock.locked()) {
- StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
- strerror(-lock.status), lock.status);
- }
-
- if (const auto it = dumpers.find(flag); it != dumpers.end()) {
- (it->second)(args, asProto, result);
- dumpLayers = false;
- } else if (!asProto) {
- dumpAllLocked(args, compositionLayers, result);
- }
- }
-
- if (dumpLayers) {
- perfetto::protos::LayersTraceFileProto traceFileProto =
- mLayerTracing.createTraceFileProto();
- perfetto::protos::LayersSnapshotProto* layersTrace = traceFileProto.add_entry();
- perfetto::protos::LayersProto layersProto = dumpProtoFromMainThread();
- layersTrace->mutable_layers()->Swap(&layersProto);
- auto displayProtos = dumpDisplayProto();
- layersTrace->mutable_displays()->Swap(&displayProtos);
-
- if (asProto) {
- result.append(traceFileProto.SerializeAsString());
- } else {
- // Dump info that we need to access from the main thread
- const auto layerTree = LayerProtoParser::generateLayerTree(layersTrace->layers());
- result.append(LayerProtoParser::layerTreeToString(layerTree));
- result.append("\n");
- dumpOffscreenLayers(result);
- }
- }
+ write(fd, result.c_str(), result.size());
+ return NO_ERROR;
}
+ if (asProto && args.empty()) {
+ perfetto::protos::LayersTraceFileProto traceFileProto =
+ mLayerTracing.createTraceFileProto();
+ perfetto::protos::LayersSnapshotProto* layersTrace = traceFileProto.add_entry();
+ perfetto::protos::LayersProto layersProto = dumpProtoFromMainThread();
+ layersTrace->mutable_layers()->Swap(&layersProto);
+ auto displayProtos = dumpDisplayProto();
+ layersTrace->mutable_displays()->Swap(&displayProtos);
+ result.append(traceFileProto.SerializeAsString());
+ write(fd, result.c_str(), result.size());
+ return NO_ERROR;
+ }
+
+ static const std::unordered_map<std::string, Dumper> dumpers = {
+ {"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
+ {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
+ {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
+ {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
+ {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
+ {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+ {"--frontend"s, mainThreadDumper(&SurfaceFlinger::dumpFrontEnd)},
+ {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
+ {"--hwclayers"s, mainThreadDumper(&SurfaceFlinger::dumpHwcLayersMinidump)},
+ {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
+ {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
+ {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
+ {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
+ {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
+ {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)},
+ {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+ };
+
+ const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
+ if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+ (it->second)(args, asProto, result);
+ write(fd, result.c_str(), result.size());
+ return NO_ERROR;
+ }
+
+ // Traversal of drawing state must happen on the main thread.
+ // Otherwise, SortedVector may have shared ownership during concurrent
+ // traversals, which can result in use-after-frees.
+ std::string compositionLayers;
+ mScheduler
+ ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ dumpVisibleFrontEnd(compositionLayers);
+ })
+ .get();
+ dumpAll(args, compositionLayers, result);
write(fd, result.c_str(), result.size());
return NO_ERROR;
}
@@ -6297,37 +6318,81 @@
}
void SurfaceFlinger::dumpFrontEnd(std::string& result) {
- mScheduler
- ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
- std::ostringstream out;
- out << "\nComposition list\n";
- ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
- lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ std::ostringstream out;
+ out << "\nComposition list\n";
+ ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << *snapshot << "\n";
+ }
+
+ out << "\nInput list\n";
+ lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << snapshot << "\n";
+ });
+
+ out << "\nLayer Hierarchy\n"
+ << mLayerHierarchyBuilder.getHierarchy().dump() << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy().dump() << "\n\n";
+ result.append(out.str());
+}
+
+void SurfaceFlinger::dumpVisibleFrontEnd(std::string& result) {
+ if (!mLayerLifecycleManagerEnabled) {
+ StringAppendF(&result, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto* compositionState = layer->getCompositionState();
+ if (!compositionState || !compositionState->isVisible) return;
+ android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
+ layer->getDebugName() ? layer->getDebugName()
+ : "<unknown>");
+ compositionState->dump(result);
+ });
+
+ StringAppendF(&result, "Offscreen Layers\n");
+ for (Layer* offscreenLayer : mOffscreenLayers) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
+ }
+ } else {
+ std::ostringstream out;
+ out << "\nComposition list\n";
+ ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (snapshot->hasSomethingToDraw()) {
+ if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << *snapshot << "\n";
}
- out << " " << *snapshot << "\n";
- }
+ });
- out << "\nInput list\n";
- lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachInputSnapshot(
- [&](const frontend::LayerSnapshot& snapshot) {
- if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
- lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
- }
- out << " " << snapshot << "\n";
- });
+ out << "\nInput list\n";
+ lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << snapshot << "\n";
+ });
- out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy().dump()
- << "\nOffscreen Hierarchy\n"
- << mLayerHierarchyBuilder.getOffscreenHierarchy().dump() << "\n\n";
- result.append(out.str());
- })
- .get();
+ out << "\nLayer Hierarchy\n"
+ << mLayerHierarchyBuilder.getHierarchy() << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
+ result = out.str();
+ dumpHwcLayersMinidump(result);
+ }
}
perfetto::protos::LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
@@ -6410,7 +6475,7 @@
}
perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
- return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
+ return mScheduler->schedule([=, this] { return dumpDrawingStateProto(traceFlags); }).get();
}
void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
@@ -6428,7 +6493,7 @@
}
void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const {
- for (const auto& [token, display] : mDisplays) {
+ for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
@@ -6445,7 +6510,10 @@
}
void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
- for (const auto& [token, display] : mDisplays) {
+ if (!mLayerLifecycleManagerEnabled) {
+ return dumpHwcLayersMinidumpLockedLegacy(result);
+ }
+ for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
@@ -6462,16 +6530,23 @@
return;
}
auto it = mLegacyLayers.find(snapshot.sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot.getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
it->second->miniDump(result, snapshot, ref);
});
result.append("\n");
}
}
-void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
- std::string& result) const {
+void SurfaceFlinger::dumpAll(const DumpArgs& args, const std::string& compositionLayers,
+ std::string& result) const {
+ TimedLock lock(mStateLock, s2ns(1), __func__);
+ if (!lock.locked()) {
+ StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
+ strerror(-lock.status), lock.status);
+ }
+
const bool colorize = !args.empty() && args[0] == String16("--color");
Colorizer colorizer(colorize);
@@ -7320,8 +7395,8 @@
if (!updateOverlay) return;
// Update the overlay on the main thread to avoid race conditions with
- // RefreshRateSelector::getActiveMode
- static_cast<void>(mScheduler->schedule([=] {
+ // RefreshRateSelector::getActiveMode.
+ static_cast<void>(mScheduler->schedule([=, this] {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
ALOGW("%s: default display is null", __func__);
@@ -7762,7 +7837,7 @@
}
bool childrenOnly = args.childrenOnly;
- RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
+ RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() -> std::unique_ptr<RenderArea> {
ui::Transform layerTransform;
Rect layerBufferSize;
if (mLayerLifecycleManagerEnabled) {
@@ -7901,7 +7976,7 @@
ATRACE_CALL();
auto future = mScheduler->schedule(
- [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
+ [=, this, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
ScreenCaptureResults captureResults;
std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
@@ -8326,7 +8401,7 @@
return BAD_VALUE;
}
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -8443,17 +8518,25 @@
return genericLayerMetadataKeyMap;
}
-status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) {
+status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate) {
PhysicalDisplayId displayId = [&]() {
Mutex::Autolock lock(mStateLock);
return getDefaultDisplayDeviceLocked()->getPhysicalId();
}();
- mScheduler->setGameModeRefreshRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
+ mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
return NO_ERROR;
}
+status_t SurfaceFlinger::setGameDefaultFrameRateOverride(uid_t uid, float frameRate) {
+ if (FlagManager::getInstance().game_default_frame_rate()) {
+ mScheduler->setGameDefaultFrameRateForUid(
+ FrameRateOverride{static_cast<uid_t>(uid), frameRate});
+ }
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::updateSmallAreaDetection(
std::vector<std::pair<int32_t, float>>& appIdThresholdMappings) {
mScheduler->updateSmallAreaDetection(appIdThresholdMappings);
@@ -8673,6 +8756,40 @@
return NO_ERROR;
}
+void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel,
+ int32_t maxLevel) {
+ if (!FlagManager::getInstance().connected_display()) {
+ return;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto idOpt = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
+ if (!idOpt) {
+ ALOGE("No display found for HDCP level changed event: connected=%d, max=%d for "
+ "display=%" PRIu64,
+ connectedLevel, maxLevel, hwcDisplayId);
+ return;
+ }
+
+ const bool isInternalDisplay =
+ mPhysicalDisplays.get(*idOpt).transform(&PhysicalDisplay::isInternal).value_or(false);
+ if (isInternalDisplay) {
+ ALOGW("Unexpected HDCP level changed for internal display: connected=%d, max=%d for "
+ "display=%" PRIu64,
+ connectedLevel, maxLevel, hwcDisplayId);
+ return;
+ }
+
+ static_cast<void>(mScheduler->schedule([this, displayId = *idOpt, connectedLevel, maxLevel]() {
+ if (const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayId))) {
+ Mutex::Autolock lock(mStateLock);
+ display->setSecure(connectedLevel >= 2 /* HDCP_V1 */);
+ }
+ mScheduler->onHdcpLevelsChanged(mAppConnectionHandle, displayId, connectedLevel, maxLevel);
+ }));
+}
+
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
BufferData& bufferData, const char* layerName, uint64_t transactionId) {
if (bufferData.buffer &&
@@ -8846,9 +8963,9 @@
}
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
auto& legacyLayer = it->second;
sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence);
@@ -8917,9 +9034,9 @@
}
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
Layer* legacyLayer = (it == mLegacyLayers.end()) ? nullptr : it->second.get();
sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name);
layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
@@ -9803,13 +9920,25 @@
return binder::Status::ok();
}
-binder::Status SurfaceComposerAIDL::setOverrideFrameRate(int32_t uid, float frameRate) {
+binder::Status SurfaceComposerAIDL::setGameModeFrameRateOverride(int32_t uid, float frameRate) {
status_t status;
const int c_uid = IPCThreadState::self()->getCallingUid();
if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
- status = mFlinger->setOverrideFrameRate(uid, frameRate);
+ status = mFlinger->setGameModeFrameRateOverride(uid, frameRate);
} else {
- ALOGE("setOverrideFrameRate() permission denied for uid: %d", c_uid);
+ ALOGE("setGameModeFrameRateOverride() permission denied for uid: %d", c_uid);
+ status = PERMISSION_DENIED;
+ }
+ return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setGameDefaultFrameRateOverride(int32_t uid, float frameRate) {
+ status_t status;
+ const int c_uid = IPCThreadState::self()->getCallingUid();
+ if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
+ status = mFlinger->setGameDefaultFrameRateOverride(uid, frameRate);
+ } else {
+ ALOGE("setGameDefaultFrameRateOverride() permission denied for uid: %d", c_uid);
status = PERMISSION_DENIED;
}
return binderStatusFromStatusT(status);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6909055..4440c7b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -21,6 +21,8 @@
* NOTE: Make sure this file doesn't include anything from <gl/ > or <gl2/ >
*/
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
#include <android/gui/BnSurfaceComposer.h>
#include <android/gui/DisplayStatInfo.h>
@@ -77,6 +79,7 @@
#include "FrontEnd/LayerSnapshotBuilder.h"
#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
+#include "MutexUtils.h"
#include "Scheduler/ISchedulerCallback.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/RefreshRateStats.h"
@@ -478,22 +481,46 @@
return std::bind(std::forward<F>(dump), _3);
}
+ Dumper lockedDumper(Dumper dump) {
+ return [this, dump](const DumpArgs& args, bool asProto, std::string& result) -> void {
+ TimedLock lock(mStateLock, s2ns(1), __func__);
+ if (!lock.locked()) {
+ base::StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
+ strerror(-lock.status), lock.status);
+ }
+ dump(args, asProto, result);
+ };
+ }
+
template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
Dumper dumper(F dump) {
using namespace std::placeholders;
- return std::bind(dump, this, _3);
+ return lockedDumper(std::bind(dump, this, _3));
}
template <typename F>
Dumper argsDumper(F dump) {
using namespace std::placeholders;
- return std::bind(dump, this, _1, _3);
+ return lockedDumper(std::bind(dump, this, _1, _3));
}
template <typename F>
Dumper protoDumper(F dump) {
using namespace std::placeholders;
- return std::bind(dump, this, _1, _2, _3);
+ return lockedDumper(std::bind(dump, this, _1, _2, _3));
+ }
+
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper mainThreadDumper(F dump) {
+ using namespace std::placeholders;
+ Dumper dumper = std::bind(dump, this, _3);
+ return [this, dumper](const DumpArgs& args, bool asProto, std::string& result) -> void {
+ mScheduler
+ ->schedule(
+ [&args, asProto, &result, dumper]() FTL_FAKE_GUARD(kMainThreadContext)
+ FTL_FAKE_GUARD(mStateLock) { dumper(args, asProto, result); })
+ .get();
+ };
}
// Maximum allowed number of display frames that can be set through backdoor
@@ -607,7 +634,9 @@
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const gui::FrameTimelineInfo& frameTimelineInfo);
- status_t setOverrideFrameRate(uid_t uid, float frameRate);
+ status_t setGameModeFrameRateOverride(uid_t uid, float frameRate);
+
+ status_t setGameDefaultFrameRateOverride(uid_t uid, float frameRate);
status_t updateSmallAreaDetection(std::vector<std::pair<int32_t, float>>& uidThresholdMappings);
@@ -625,6 +654,8 @@
status_t getStalledTransactionInfo(
int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result);
+ void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
+
// Implements IBinder::DeathRecipient.
void binderDied(const wp<IBinder>& who) override;
@@ -696,9 +727,9 @@
void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
- // TODO(b/241285191): Replace DisplayDevice with DisplayModeRequest, and move to Scheduler.
- void dropModeRequest(const sp<DisplayDevice>&) REQUIRES(mStateLock);
- void applyActiveMode(const sp<DisplayDevice>&) REQUIRES(mStateLock);
+ // TODO(b/241285191): Move to Scheduler.
+ void dropModeRequest(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+ void applyActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
// Called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
@@ -1080,8 +1111,8 @@
/*
* Debugging & dumpsys
*/
- void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
- std::string& result) const REQUIRES(mStateLock);
+ void dumpAll(const DumpArgs& args, const std::string& compositionLayers,
+ std::string& result) const EXCLUDES(mStateLock);
void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext);
void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
@@ -1103,7 +1134,8 @@
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
void dumpHdrInfo(std::string& result) const REQUIRES(mStateLock);
- void dumpFrontEnd(std::string& result);
+ void dumpFrontEnd(std::string& result) REQUIRES(kMainThreadContext);
+ void dumpVisibleFrontEnd(std::string& result) REQUIRES(mStateLock, kMainThreadContext);
perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto,
@@ -1246,6 +1278,7 @@
hal::Connection connection = hal::Connection::INVALID;
};
+ bool mIsHdcpViaNegVsync = false;
bool mIsHotplugErrViaNegVsync = false;
std::mutex mHotplugMutex;
@@ -1439,7 +1472,7 @@
bool mLegacyFrontEndEnabled = true;
frontend::LayerLifecycleManager mLayerLifecycleManager;
- frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
+ frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
frontend::LayerSnapshotBuilder mLayerSnapshotBuilder;
std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles;
@@ -1460,6 +1493,19 @@
// Map of displayid to mirrorRoot
ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
+ // NotifyExpectedPresentHint
+ struct NotifyExpectedPresentData {
+ // lastExpectedPresentTimestamp is read and write from multiple threads such as
+ // main thread, EventThread, MessageQueue. And is atomic for that reason.
+ std::atomic<TimePoint> lastExpectedPresentTimestamp{};
+ Fps lastFrameInterval{};
+ };
+ std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap;
+
+ void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime, Fps frameInterval,
+ std::optional<Period> timeoutOpt);
+
void sfdo_enableRefreshRateOverlay(bool active);
void sfdo_setDebugFlash(int delay);
void sfdo_scheduleComposite();
@@ -1571,7 +1617,8 @@
binder::Status getDisplayDecorationSupport(
const sp<IBinder>& displayToken,
std::optional<gui::DisplayDecorationSupport>* outSupport) override;
- binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
+ binder::Status setGameModeFrameRateOverride(int32_t uid, float frameRate) override;
+ binder::Status setGameDefaultFrameRateOverride(int32_t uid, float frameRate) override;
binder::Status enableRefreshRateOverlay(bool active) override;
binder::Status setDebugFlash(int delay) override;
binder::Status scheduleComposite() override;
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index ce8119e..696f348 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -438,6 +438,7 @@
for (auto& [layerStack, displayInfo] : mStartingDisplayInfos) {
entryProto.mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
}
+ entryProto.set_displays_changed(true);
return entryProto;
}
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index ddbf3e4..6a66fff 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -218,6 +218,13 @@
friend class Singleton<TransactionTracing>;
std::function<void(const std::string& prefix, bool overwrite)> mWriterFunction =
[](const std::string&, bool) {};
+ std::atomic<bool> mEnabled{true};
+
+ void doInvoke(const std::string& filename, bool overwrite) {
+ if (mEnabled) {
+ mWriterFunction(filename, overwrite);
+ }
+ };
public:
void setWriterFunction(
@@ -225,12 +232,15 @@
mWriterFunction = std::move(function);
}
void invoke(const std::string& prefix, bool overwrite) {
- mWriterFunction(TransactionTracing::getFilePath(prefix), overwrite);
+ doInvoke(TransactionTracing::getFilePath(prefix), overwrite);
}
/* pass in a complete file path for testing */
void invokeForTest(const std::string& filename, bool overwrite) {
- mWriterFunction(filename, overwrite);
+ doInvoke(filename, overwrite);
}
+ /* hacky way to avoid generating traces when converting transaction trace to layers trace. */
+ void disable() { mEnabled.store(false); }
+ void enable() { mEnabled.store(true); }
};
} // namespace android
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index c2d1954..617ea2c 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -40,9 +40,24 @@
namespace android {
using namespace ftl::flag_operators;
+namespace {
+class ScopedTraceDisabler {
+public:
+ ScopedTraceDisabler() { TransactionTraceWriter::getInstance().disable(); }
+ ~ScopedTraceDisabler() { TransactionTraceWriter::getInstance().enable(); }
+};
+} // namespace
+
bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& traceFile,
std::uint32_t traceFlags, LayerTracing& layerTracing,
bool onlyLastEntry) {
+ // We are generating the layers trace by replaying back a set of transactions. If the
+ // transactions have unexpected states, we may generate a transaction trace to debug
+ // the unexpected state. This is silly. So we disable it by poking the
+ // TransactionTraceWriter. This is really a hack since we should manage our depenecies a
+ // little better.
+ ScopedTraceDisabler fatalErrorTraceDisabler;
+
if (traceFile.entry_size() == 0) {
ALOGD("Trace file is empty");
return false;
@@ -52,7 +67,7 @@
// frontend
frontend::LayerLifecycleManager lifecycleManager;
- frontend::LayerHierarchyBuilder hierarchyBuilder{{}};
+ frontend::LayerHierarchyBuilder hierarchyBuilder;
frontend::LayerSnapshotBuilder snapshotBuilder;
ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
@@ -119,12 +134,10 @@
lifecycleManager.applyTransactions(transactions, /*ignoreUnknownHandles=*/true);
lifecycleManager.onHandlesDestroyed(destroyedHandles, /*ignoreUnknownHandles=*/true);
- if (lifecycleManager.getGlobalChanges().test(
- frontend::RequestedLayerState::Changes::Hierarchy)) {
- hierarchyBuilder.update(lifecycleManager.getLayers(),
- lifecycleManager.getDestroyedLayers());
- }
+ // update hierarchy
+ hierarchyBuilder.update(lifecycleManager);
+ // update snapshots
frontend::LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(),
.layerLifecycleManager = lifecycleManager,
.displays = displayInfos,
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index a8db0fa..a27e100 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -126,7 +126,8 @@
DUMP_READ_ONLY_FLAG(enable_fro_dependent_features);
DUMP_READ_ONLY_FLAG(display_protected);
DUMP_READ_ONLY_FLAG(fp16_client_target);
-
+ DUMP_READ_ONLY_FLAG(game_default_frame_rate);
+ DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
@@ -199,6 +200,8 @@
FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "")
FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "")
FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
+FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
+FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "")
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 7edcb1d..2f2895c 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -65,6 +65,8 @@
bool enable_fro_dependent_features() const;
bool display_protected() const;
bool fp16_client_target() const;
+ bool game_default_frame_rate() const;
+ bool enable_layer_command_batching() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index b690d8d..649ad25 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -134,13 +134,13 @@
dispatch->schedule(tmp,
{.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()});
},
"o.o");
dispatch->schedule(tmp,
{.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()});
dispatch->unregisterCallback(tmp);
dispatch->cancel(tmp);
}
@@ -162,20 +162,20 @@
entry.update(*stubTracker, 0);
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()},
*stubTracker, 0);
entry.disarm();
entry.ensureNotRunning();
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()},
*stubTracker, 0);
auto const wakeup = entry.wakeupTime();
auto const ready = entry.readyTime();
entry.callback(entry.executing(), *wakeup, *ready);
entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()});
dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp);
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index fa307e9..114f3b0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -86,7 +86,10 @@
bool addVsyncTimestamp(nsecs_t /* timestamp */) override { return true; }
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */) const override { return 1; }
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */,
+ std::optional<nsecs_t>) const override {
+ return 1;
+ }
nsecs_t currentPeriod() const override { return 1; }
Period minFramePeriod() const override { return Period::fromNs(currentPeriod()); }
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 9fc329f..b65a2b3 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -32,6 +32,14 @@
}
flag {
+ name: "enable_layer_command_batching"
+ namespace: "core_graphics"
+ description: "This flag controls batching on createLayer/destroyLayer command with executeCommand."
+ bug: "290685621"
+ is_fixed_read_only: true
+}
+
+flag {
name: "dont_skip_on_early"
namespace: "core_graphics"
description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
@@ -133,3 +141,11 @@
bug: "236745178"
is_fixed_read_only: true
}
+
+flag {
+ name: "game_default_frame_rate"
+ namespace: "game"
+ description: "This flag guards the new behavior with the addition of Game Default Frame Rate feature."
+ bug: "286084594"
+ is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 5888a55..5449aeb 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -55,7 +55,6 @@
"ReleaseBufferCallback_test.cpp",
"ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
- "SetFrameRateOverride_test.cpp",
"SetGeometry_test.cpp",
"Stress_test.cpp",
"TextureFiltering_test.cpp",
diff --git a/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp b/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp
deleted file mode 100644
index e43ef95..0000000
--- a/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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 <android/gui/ISurfaceComposer.h>
-#include <gtest/gtest.h>
-#include <gui/DisplayEventReceiver.h>
-#include <gui/SurfaceComposerClient.h>
-#include <sys/epoll.h>
-#include <algorithm>
-
-namespace android {
-namespace {
-using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
-using gui::ISurfaceComposer;
-
-class SetFrameRateOverrideTest : public ::testing::Test {
-protected:
- void SetUp() override {
- const ISurfaceComposer::VsyncSource vsyncSource =
- ISurfaceComposer::VsyncSource::eVsyncSourceApp;
- const EventRegistrationFlags eventRegistration = {
- ISurfaceComposer::EventRegistration::frameRateOverride};
-
- mDisplayEventReceiver =
- std::make_unique<DisplayEventReceiver>(vsyncSource, eventRegistration);
- EXPECT_EQ(NO_ERROR, mDisplayEventReceiver->initCheck());
-
- mEpollFd = epoll_create1(EPOLL_CLOEXEC);
- EXPECT_GT(mEpollFd, 1);
-
- epoll_event event;
- event.events = EPOLLIN;
- EXPECT_EQ(0, epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mDisplayEventReceiver->getFd(), &event));
- }
-
- void TearDown() override { close(mEpollFd); }
-
- void setFrameRateAndListenEvents(uid_t uid, float frameRate) {
- status_t ret = SurfaceComposerClient::setOverrideFrameRate(uid, frameRate);
- ASSERT_EQ(NO_ERROR, ret);
-
- DisplayEventReceiver::Event event;
- bool isOverrideFlushReceived = false;
- mFrameRateOverrides.clear();
-
- epoll_event epollEvent;
- while (epoll_wait(mEpollFd, &epollEvent, 1, 1000) > 0) {
- while (mDisplayEventReceiver->getEvents(&event, 1) > 0) {
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
- mFrameRateOverrides.emplace_back(event.frameRateOverride);
- }
- if (event.header.type ==
- DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
- isOverrideFlushReceived = true;
- }
- }
-
- if (isOverrideFlushReceived) break;
- }
- }
-
- std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
- std::vector<FrameRateOverride> mFrameRateOverrides;
-
- int mEpollFd;
-};
-
-TEST_F(SetFrameRateOverrideTest, SetFrameRateOverrideCall) {
- uid_t uid = getuid();
- float frameRate = 30.0f;
- setFrameRateAndListenEvents(uid, frameRate);
- // check if the frame rate override we set exists
- ASSERT_TRUE(std::find_if(mFrameRateOverrides.begin(), mFrameRateOverrides.end(),
- [uid = uid, frameRate = frameRate](auto i) {
- return uid == i.uid && frameRate == i.frameRateHz;
- }) != mFrameRateOverrides.end());
-
- // test removing frame rate override
- frameRate = 0.0f;
- setFrameRateAndListenEvents(uid, frameRate);
- ASSERT_TRUE(std::find_if(mFrameRateOverrides.begin(), mFrameRateOverrides.end(),
- [uid = uid, frameRate = frameRate](auto i) {
- return uid == i.uid && frameRate == i.frameRateHz;
- }) == mFrameRateOverrides.end());
-}
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index dea0194..c75f902 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -106,6 +106,7 @@
"SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
"SurfaceFlinger_InitializeDisplaysTest.cpp",
+ "SurfaceFlinger_NotifyExpectedPresentTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
"SurfaceFlinger_PowerHintTest.cpp",
"SurfaceFlinger_SetDisplayStateTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index c463a92..220001b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -23,10 +23,13 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \
- ASSERT_TRUE(requestOpt); \
- EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \
- EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent)
+#define EXPECT_DISPLAY_MODE_REQUEST(expected, request) \
+ EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, request.mode); \
+ EXPECT_EQ(expected.emitEvent, request.emitEvent)
+
+#define EXPECT_DISPLAY_MODE_REQUEST_OPT(expected, requestOpt) \
+ ASSERT_TRUE(requestOpt); \
+ EXPECT_DISPLAY_MODE_REQUEST(expected, (*requestOpt))
namespace android {
namespace {
@@ -37,6 +40,7 @@
class InitiateModeChangeTest : public DisplayTransactionTest {
public:
using Action = DisplayDevice::DesiredModeAction;
+
void SetUp() override {
injectFakeBufferQueueFactory();
injectFakeNativeWindowSurfaceFactory();
@@ -84,36 +88,43 @@
TEST_F(InitiateModeChangeTest, setDesiredMode) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode());
EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode120, mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, clearDesiredMode) {
+TEST_F(InitiateModeChangeTest, takeDesiredMode) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(kDesiredMode90, mDisplay->getDesiredMode());
- mDisplay->clearDesiredMode();
+ EXPECT_EQ(kDesiredMode90, mDisplay->takeDesiredMode());
EXPECT_FALSE(mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) {
+TEST_F(InitiateModeChangeTest, initiateModeChange) FTL_FAKE_GUARD(kMainThreadContext) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode());
const hal::VsyncPeriodChangeConstraints constraints{
.desiredTimeNanos = systemTime(),
.seamlessRequired = false,
};
hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- mDisplay->clearDesiredMode();
+ auto desiredModeOpt = mDisplay->takeDesiredMode();
+ ASSERT_TRUE(desiredModeOpt);
EXPECT_FALSE(mDisplay->getDesiredMode());
+
+ EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline));
+
+ auto modeChange = mDisplay->finalizeModeChange();
+
+ auto* changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange);
+ ASSERT_TRUE(changePtr);
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, changePtr->activeMode);
}
TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) {
@@ -125,27 +136,47 @@
TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode());
const hal::VsyncPeriodChangeConstraints constraints{
.desiredTimeNanos = systemTime(),
.seamlessRequired = false,
};
hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
-
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
-
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getPendingMode());
-
- mDisplay->clearDesiredMode();
+ auto desiredModeOpt = mDisplay->takeDesiredMode();
+ ASSERT_TRUE(desiredModeOpt);
EXPECT_FALSE(mDisplay->getDesiredMode());
+
+ EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline));
+
+ auto modeChange = mDisplay->finalizeModeChange();
+ auto* changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange);
+ ASSERT_TRUE(changePtr);
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, changePtr->activeMode);
+
+ // The latest request should override the desired mode.
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode60)));
+ EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
+
+ EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode120, mDisplay->getDesiredMode());
+
+ // No pending mode change.
+ EXPECT_TRUE(
+ std::holds_alternative<DisplayDevice::NoModeChange>(mDisplay->finalizeModeChange()));
+
+ desiredModeOpt = mDisplay->takeDesiredMode();
+ ASSERT_TRUE(desiredModeOpt);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+
+ EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline));
+
+ modeChange = mDisplay->finalizeModeChange();
+
+ changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange);
+ ASSERT_TRUE(changePtr);
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, changePtr->activeMode);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index ee12276..6671414 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -481,14 +481,17 @@
constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
-template <typename PhysicalDisplay, int width, int height>
+template <typename PhysicalDisplay, int width, int height,
+ Secure secure = (PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal)
+ ? Secure::TRUE
+ : Secure::FALSE>
struct PhysicalDisplayVariant
- : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE,
- Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY,
+ : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, secure,
+ PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY,
PHYSICAL_DISPLAY_FLAGS>,
HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
- Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ Async::FALSE, secure, PhysicalDisplay::PRIMARY,
GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
PhysicalDisplay> {};
@@ -515,6 +518,7 @@
};
struct TertiaryDisplay {
+ static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External;
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr uint8_t PORT = 253;
static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 8891c06..4e8a609 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -471,7 +471,7 @@
mock::VSyncTracker& mockTracker =
*static_cast<mock::VSyncTracker*>(&mVsyncSchedule->getTracker());
- EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_, _))
.WillOnce(Return(preferredExpectedPresentationTime));
VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection);
diff --git a/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
index a581c7a..7c1d4b4 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
@@ -17,6 +17,8 @@
#undef LOG_TAG
#define LOG_TAG "FrameRateOverrideMappingsTest"
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <common/test/FlagUtils.h>
#include <gtest/gtest.h>
#include <unordered_map>
@@ -34,6 +36,8 @@
};
namespace {
+using namespace com::android::graphics::surfaceflinger;
+
TEST_F(FrameRateOverrideMappingsTest, testUpdateFrameRateOverridesByContent) {
mFrameRateOverrideByContent.clear();
mFrameRateOverrideByContent.emplace(0, 30.0_Hz);
@@ -59,6 +63,8 @@
}
TEST_F(FrameRateOverrideMappingsTest, testSetGameModeRefreshRateForUid) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, false);
+
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({1, 30.0f});
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({2, 90.0f});
@@ -95,6 +101,7 @@
}
TEST_F(FrameRateOverrideMappingsTest, testGetFrameRateOverrideForUidMixed) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, false);
mFrameRateOverrideByContent.clear();
mFrameRateOverrideByContent.emplace(0, 30.0_Hz);
mFrameRateOverrideByContent.emplace(1, 60.0_Hz);
@@ -111,7 +118,6 @@
ASSERT_EQ(allFrameRateOverrides,
mFrameRateOverrideMappings.getAllFrameRateOverrides(
/*supportsFrameRateOverrideByContent*/ true));
-
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({1, 30.0f});
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({2, 90.0f});
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({4, 120.0f});
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index d3ce4f2..6edecff 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -82,15 +82,6 @@
EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE));
EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
}
-
- void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp,
- Fps lastFrameInterval) {
- ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end());
- auto& displayData = mHwc.mDisplayData.at(displayId);
- std::scoped_lock lock{displayData.expectedPresentLock};
- displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
- displayData.lastFrameInterval = lastFrameInterval;
- }
};
TEST_F(HWComposerTest, isHeadless) {
@@ -143,7 +134,7 @@
const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
ASSERT_TRUE(info);
- EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false));
{
EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
@@ -235,7 +226,7 @@
const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
ASSERT_TRUE(info);
- EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false));
{
EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
@@ -324,7 +315,7 @@
const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
ASSERT_TRUE(info);
- EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(true));
{
EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
@@ -417,144 +408,6 @@
EXPECT_FALSE(displayIdOpt);
}
-TEST_F(HWComposerTest, notifyExpectedPresentTimeout) {
- constexpr hal::HWDisplayId kHwcDisplayId = 2;
- expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
- ASSERT_TRUE(info);
-
- auto expectedPresentTime = systemTime() + ms2ns(10);
- static constexpr Fps Fps60Hz = 60_Hz;
- static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs();
- static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs();
- static constexpr Period kVsyncPeriod =
- Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
- static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs);
- static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0);
-
- ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz));
-
- {
- // Very first ExpectedPresent after idle, no previous timestamp
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
- .WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), Fps60Hz,
- kTimeoutNs);
- }
- {
- // Absent timeoutNs
- expectedPresentTime += 2 * kFrameInterval5HzNs;
- EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), Fps60Hz,
- /*timeoutOpt*/ std::nullopt);
- }
- {
- // Timeout is 0
- expectedPresentTime += kFrameInterval60HzNs;
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
- .WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), Fps60Hz,
- Period::fromNs(0));
- }
- {
- // ExpectedPresent is after the timeoutNs
- expectedPresentTime += 2 * kFrameInterval5HzNs;
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
- .WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), Fps60Hz,
- kTimeoutNs);
- }
- {
- // ExpectedPresent has not changed
- EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), Fps60Hz,
- kTimeoutNs);
- }
- {
- // ExpectedPresent is after the last reported ExpectedPresent.
- expectedPresentTime += kFrameInterval60HzNs;
- EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), Fps60Hz,
- kTimeoutNs);
- }
- {
- // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs,
- // representing we changed our decision and want to present earlier than previously
- // reported.
- expectedPresentTime -= kFrameInterval120HzNs;
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
- .WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), Fps60Hz,
- kTimeoutNs);
- }
-}
-
-TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) {
- constexpr hal::HWDisplayId kHwcDisplayId = 2;
- expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
- ASSERT_TRUE(info);
-
- const auto now = systemTime();
- auto expectedPresentTime = now;
- static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
-
- ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0)));
- static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs();
- static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs();
- static constexpr Period kVsyncPeriod =
- Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
-
- struct FrameRateIntervalTestData {
- int32_t frameIntervalNs;
- bool callExpectedPresent;
- };
- const std::vector<FrameRateIntervalTestData> frameIntervals = {
- {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true},
- {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true},
- {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true},
- {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false},
- {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true},
- {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true},
- {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true},
- };
-
- for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) {
- {
- expectedPresentTime += frameIntervalNs;
- if (callExpectedPresent) {
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime,
- frameIntervalNs))
- .WillOnce(Return(HalError::NONE));
- } else {
- EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
- }
- mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime),
- Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs);
- }
- }
-}
-
struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
MOCK_METHOD(void, onComposerHalHotplugEvent, (hal::HWDisplayId, DisplayHotplugEvent),
(override));
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 95f1940..2b333f4 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -45,7 +45,8 @@
// reparenting tests
TEST_F(LayerHierarchyTest, addLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -64,7 +65,8 @@
}
TEST_F(LayerHierarchyTest, reparentLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(2, 11);
reparentLayer(111, 12);
reparentLayer(1221, 1);
@@ -79,7 +81,8 @@
}
TEST_F(LayerHierarchyTest, reparentLayerToNull) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(2, UNASSIGNED_LAYER_ID);
reparentLayer(11, UNASSIGNED_LAYER_ID);
@@ -96,7 +99,8 @@
}
TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(2, UNASSIGNED_LAYER_ID);
reparentLayer(11, UNASSIGNED_LAYER_ID);
reparentLayer(1221, UNASSIGNED_LAYER_ID);
@@ -115,7 +119,8 @@
}
TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
destroyLayerHandle(111);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -139,7 +144,8 @@
}
TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
reparentLayer(11, 1);
@@ -154,7 +160,8 @@
// offscreen tests
TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -170,7 +177,8 @@
}
TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -187,7 +195,8 @@
// rel-z tests
TEST_F(LayerHierarchyTest, setRelativeParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -200,7 +209,8 @@
}
TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -216,7 +226,8 @@
}
TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -231,7 +242,8 @@
}
TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -246,7 +258,8 @@
}
TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -262,7 +275,8 @@
}
TEST_F(LayerHierarchyTest, reparentRelativeLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -294,7 +308,8 @@
// mirror tests
TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -308,7 +323,8 @@
}
TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
@@ -324,7 +340,8 @@
TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
createLayer(1111, 111);
createLayer(112, 11);
@@ -340,7 +357,8 @@
// mirror & relatives tests
TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(111, 12);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
@@ -371,7 +389,8 @@
}
TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(1221, 12);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
@@ -401,7 +420,8 @@
}
TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
reparentLayer(2, UNASSIGNED_LAYER_ID);
@@ -427,7 +447,8 @@
}
TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(1221, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -462,7 +483,8 @@
}
TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
updateBackgroundColor(1, 0.5);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -485,7 +507,8 @@
createLayer(11, 1);
reparentLayer(1, 11);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
std::vector<uint32_t> expectedTraversalPath = {};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -502,17 +525,11 @@
createLayer(11, 1);
reparentRelativeLayer(11, 2);
reparentRelativeLayer(2, 11);
- mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
-
- // fix loop
- uint32_t invalidRelativeRoot;
- bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
- EXPECT_TRUE(hasRelZLoop);
- mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
- hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
- EXPECT_EQ(invalidRelativeRoot, 11u);
- EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+ LayerHierarchyBuilder hierarchyBuilder;
+ // this call is expected to fix the loop!
+ hierarchyBuilder.update(mLifecycleManager);
+ uint32_t unused;
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused));
std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -534,16 +551,11 @@
createLayer(221, 22);
reparentRelativeLayer(22, 111);
reparentRelativeLayer(11, 221);
- mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
-
- // fix loop
- uint32_t invalidRelativeRoot;
- bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
- EXPECT_TRUE(hasRelZLoop);
- mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
- hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
- EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+ LayerHierarchyBuilder hierarchyBuilder;
+ // this call is expected to fix the loop!
+ hierarchyBuilder.update(mLifecycleManager);
+ uint32_t unused;
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused));
std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -554,7 +566,8 @@
}
TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(1, UNASSIGNED_LAYER_ID);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -568,7 +581,8 @@
TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
// remove default hierarchy
mLifecycleManager = LayerLifecycleManager();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
createRootLayer(1);
destroyLayerHandle(1);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -582,7 +596,8 @@
// traversal path test
TEST_F(LayerHierarchyTest, traversalPathId) {
setZ(122, -1);
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
auto checkTraversalPathIdVisitor =
[](const LayerHierarchy& hierarchy,
const LayerHierarchy::TraversalPath& traversalPath) -> bool {
@@ -605,7 +620,8 @@
createLayer(53, 5);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
UPDATE_AND_VERIFY(hierarchyBuilder);
std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 4, 5, 51, 53};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -639,7 +655,8 @@
setZ(13, 1);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
UPDATE_AND_VERIFY(hierarchyBuilder);
std::vector<uint32_t> expectedTraversalPath = {1, 11, 13, 12};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -661,7 +678,8 @@
setLayerStack(2, 10);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
UPDATE_AND_VERIFY(hierarchyBuilder);
std::vector<uint32_t> expectedTraversalPath = {2, 21, 1, 11};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -672,7 +690,8 @@
}
TEST_F(LayerHierarchyTest, canMirrorDisplay) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -687,7 +706,8 @@
}
TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(5));
setLayerStack(3, 1);
@@ -701,7 +721,8 @@
}
TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -719,7 +740,8 @@
}
TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -737,7 +759,8 @@
}
TEST_F(LayerHierarchyTest, canMirrorDisplayWithMirrors) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(12, UNASSIGNED_LAYER_ID);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
UPDATE_AND_VERIFY(hierarchyBuilder);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 7e9abce..67e6249 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -176,14 +176,12 @@
void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); }
void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
- if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
- hierarchyBuilder.update(mLifecycleManager.getLayers(),
- mLifecycleManager.getDestroyedLayers());
- }
+ hierarchyBuilder.update(mLifecycleManager);
mLifecycleManager.commitChanges();
// rebuild layer hierarchy from scratch and verify that it matches the updated state.
- LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder newBuilder;
+ newBuilder.update(mLifecycleManager);
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
getTraversalPath(newBuilder.getHierarchy()));
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
@@ -512,10 +510,7 @@
}
void update(LayerSnapshotBuilder& snapshotBuilder) {
- if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
- mHierarchyBuilder.update(mLifecycleManager.getLayers(),
- mLifecycleManager.getDestroyedLayers());
- }
+ mHierarchyBuilder.update(mLifecycleManager);
LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
.layerLifecycleManager = mLifecycleManager,
.includeMetadata = false,
@@ -530,7 +525,7 @@
mLifecycleManager.commitChanges();
}
- LayerHierarchyBuilder mHierarchyBuilder{{}};
+ LayerHierarchyBuilder mHierarchyBuilder;
DisplayInfos mFrontEndDisplayInfos;
bool mHasDisplayChanges = false;
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 787fa1c..e9d2319 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -56,7 +56,7 @@
LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
mFlinger.resetScheduler(mScheduler);
mLifecycleManager = {};
- mHierarchyBuilder = {{}};
+ mHierarchyBuilder = {};
}
void updateLayerSnapshotsAndLayerHistory(nsecs_t now) {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index b88ef56..0ae3ca3 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -118,6 +118,9 @@
auto createLayer(std::string name) {
return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
}
+ auto createLayer(std::string name, uint32_t uid) {
+ return sp<MockLayer>::make(mFlinger.flinger(), std::move(name), std::move(uid));
+ }
void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
Fps desiredRefreshRate, int numFrames) {
@@ -247,6 +250,105 @@
}
}
+TEST_F(LayerHistoryTest, gameFrameRateOverrideMapping) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
+
+ auto overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(120_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+}
+
+TEST_F(LayerHistoryTest, oneLayerGameFrameRateOverride) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ const uid_t uid = 0;
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const Fps gameModeFrameRate = Fps::fromValue(60.0f);
+ const auto layer = createLayer("GameFrameRateLayer", uid);
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ // update game default frame rate override
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ nsecs_t time = systemTime();
+ LayerHistory::Summary summary;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += gameDefaultFrameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
+
+ // test against setFrameRate vote
+ const Fps setFrameRate = Fps::fromValue(120.0f);
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default)));
+
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += setFrameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
+
+ // update game mode frame rate override
+ history().updateGameModeFrameRateOverride(
+ FrameRateOverride({uid, gameModeFrameRate.getValue()}));
+
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += gameModeFrameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
+}
+
TEST_F(LayerHistoryTest, oneInvisibleLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 50cd784..ba32c68 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -58,8 +58,7 @@
void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) {
if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
- mHierarchyBuilder.update(mLifecycleManager.getLayers(),
- mLifecycleManager.getDestroyedLayers());
+ mHierarchyBuilder.update(mLifecycleManager);
}
args.root = mHierarchyBuilder.getHierarchy();
actualBuilder.update(args);
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 9aa089f..e9c4d80 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -91,7 +91,7 @@
TEST_F(MessageQueueTest, commit) {
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
@@ -105,7 +105,7 @@
InSequence s;
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
@@ -124,7 +124,7 @@
InSequence s;
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
@@ -151,7 +151,7 @@
const auto timingAfterCallback =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = kPresentTime.ns()};
+ .lastVsync = kPresentTime.ns()};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
@@ -163,7 +163,7 @@
const auto timing =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDifferentDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index e515895..7fdca71 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -98,7 +98,7 @@
mock::VsyncTrackerCallback mVsyncTrackerCallback;
TestableScheduler* mScheduler =
new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback};
- surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
+ surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
@@ -584,17 +584,17 @@
scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
vrrTracker->addVsyncTimestamp(0);
- // Next frame at refresh rate as no previous frame
- EXPECT_EQ(refreshRate,
- scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), TimePoint::fromNs(0)));
-
EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(500)));
+ TimePoint::fromNs(1000)));
EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(1500)));
+ TimePoint::fromNs(2000)));
+ // Not crossing the min frame period
+ EXPECT_EQ(Fps::fromPeriodNsecs(1500),
+ scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
+ TimePoint::fromNs(2500)));
// Change render rate
frameRate = Fps::fromPeriodNsecs(2000);
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
@@ -602,10 +602,10 @@
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(2500)));
+ TimePoint::fromNs(2000)));
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(4500)));
+ TimePoint::fromNs(4000)));
}
TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 7ad97a2..3291dc3 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -53,7 +53,7 @@
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(Return(
TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
@@ -138,14 +138,14 @@
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(
Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
EXPECT_CALL(*vsyncTracker, minFramePeriod())
.WillRepeatedly(Return(Period::fromNs(
TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
std::move(eventThread), std::move(sfEventThread),
std::move(selectorPtr),
@@ -179,7 +179,7 @@
Mock::VerifyAndClearExpectations(mComposer);
- EXPECT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_FALSE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that the next commit will complete the mode change and send
@@ -263,11 +263,13 @@
mFlinger.commit();
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
+ // The 120 Hz mode should be pending.
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
mFlinger.commit();
+ // The 120 Hz mode should be active.
EXPECT_FALSE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
}
@@ -324,7 +326,7 @@
EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
}
-MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
+MATCHER_P2(HasDesiredMode, flinger, modeId, "") {
if (!arg->getDesiredMode()) {
*result_listener << "No desired mode";
return false;
@@ -343,12 +345,33 @@
return true;
}
-MATCHER_P(ModeSettledTo, modeId, "") {
+MATCHER_P(HasPendingMode, modeId, "") {
+ const auto pendingOpt = TestableSurfaceFlinger::getPendingMode(arg);
+
+ if (!pendingOpt) {
+ *result_listener << "No pending mode";
+ return false;
+ }
+
+ if (pendingOpt->mode.modePtr->getId() != modeId) {
+ *result_listener << "Unexpected pending mode " << modeId;
+ return false;
+ }
+
+ return true;
+}
+
+MATCHER_P(HasActiveMode, modeId, "") {
if (const auto desiredOpt = arg->getDesiredMode()) {
*result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId();
return false;
}
+ if (const auto pendingOpt = TestableSurfaceFlinger::getPendingMode(arg)) {
+ *result_listener << "Unsettled pending mode " << pendingOpt->mode.modePtr->getId();
+ return false;
+ }
+
ftl::FakeGuard guard(kMainThreadContext);
if (arg->getActiveMode().modePtr->getId() != modeId) {
@@ -365,14 +388,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
// Only the inner display is powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -384,8 +407,8 @@
mock::createDisplayModeSpecs(kModeId60.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_CALL(*mComposer,
@@ -395,13 +418,13 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
innerDisplay->setPowerMode(hal::PowerMode::OFF);
outerDisplay->setPowerMode(hal::PowerMode::ON);
@@ -409,8 +432,8 @@
// Only the outer display is powered on.
mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60));
EXPECT_CALL(*mComposer,
setActiveConfigWithConstraints(kOuterDisplayHwcId,
@@ -419,13 +442,13 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60));
}
TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
@@ -434,16 +457,16 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
outerDisplay->setPowerMode(hal::PowerMode::ON);
// Both displays are powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -455,8 +478,8 @@
mock::createDisplayModeSpecs(kModeId60.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_CALL(*mComposer,
@@ -471,25 +494,25 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) {
EXPECT_TRUE(mDisplay->isPoweredOn());
- EXPECT_THAT(mDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(mDisplay, HasActiveMode(kModeId60));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(mDisplay, HasDesiredMode(&mFlinger, kModeId90));
// Power off the display before the mode has been set.
mDisplay->setPowerMode(hal::PowerMode::OFF);
@@ -504,11 +527,11 @@
// Powering off should not abort the mode set.
EXPECT_FALSE(mDisplay->isPoweredOn());
- EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(mDisplay, HasPendingMode(kModeId90));
mFlinger.commit();
- EXPECT_THAT(mDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(mDisplay, HasActiveMode(kModeId90));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
@@ -517,16 +540,16 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
outerDisplay->setPowerMode(hal::PowerMode::ON);
// Both displays are powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -538,8 +561,8 @@
mock::createDisplayModeSpecs(kModeId60.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60));
// Power off the outer display before the mode has been set.
outerDisplay->setPowerMode(hal::PowerMode::OFF);
@@ -553,13 +576,13 @@
mFlinger.commit();
// Powering off the inactive display should abort the mode set.
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
innerDisplay->setPowerMode(hal::PowerMode::OFF);
outerDisplay->setPowerMode(hal::PowerMode::ON);
@@ -575,13 +598,13 @@
mFlinger.commit();
// The mode set should resume once the display becomes active.
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index fc5f2b0..1583f64 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -46,7 +46,7 @@
EXPECT_CALL(static_cast<mock::VSyncTracker&>(
mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
- nextAnticipatedVSyncTimeFrom(_))
+ nextAnticipatedVSyncTimeFrom(_, _))
.WillRepeatedly(Return(0));
// --------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
new file mode 100644
index 0000000..7206e29
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class NotifyExpectedPresentTest : public DisplayTransactionTest {
+public:
+ void SetUp() override {
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject();
+ FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary)
+ .setPowerMode(hal::PowerMode::ON)
+ .inject(&mFlinger, mComposer);
+ }
+
+protected:
+ sp<DisplayDevice> mDisplay;
+ static constexpr bool kIsPrimary = true;
+ static constexpr hal::HWDisplayId HWC_DISPLAY_ID =
+ FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+};
+
+TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) {
+ const auto physicDisplayId = mDisplay->getPhysicalId();
+ auto expectedPresentTime = systemTime() + ms2ns(10);
+ static constexpr Fps kFps60Hz = 60_Hz;
+ static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.getPeriodNsecs();
+ static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs();
+ static constexpr Period kVsyncPeriod =
+ Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
+ static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs);
+ static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0);
+
+ ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId,
+ kLastExpectedPresentTimestamp,
+ kFps60Hz));
+
+ {
+ // Very first ExpectedPresent after idle, no previous timestamp
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // Absent timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ /*timeoutOpt*/ std::nullopt);
+ }
+ {
+ // Timeout is 0
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ Period::fromNs(0));
+ }
+ {
+ // ExpectedPresent is after the timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent has not changed
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent is after the last reported ExpectedPresent.
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs,
+ // representing we changed our decision and want to present earlier than previously
+ // reported.
+ expectedPresentTime -= kFrameInterval120HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+}
+
+TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
+ const auto physicDisplayId = mDisplay->getPhysicalId();
+ const auto now = systemTime();
+ auto expectedPresentTime = now;
+ static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
+
+ ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId,
+ TimePoint::fromNs(now),
+ Fps::fromValue(0)));
+ static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs();
+ static constexpr Period kVsyncPeriod =
+ Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
+
+ struct FrameRateIntervalTestData {
+ int32_t frameIntervalNs;
+ bool callExpectedPresent;
+ };
+ const std::vector<FrameRateIntervalTestData> frameIntervals = {
+ {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true},
+ {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true},
+ {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true},
+ {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false},
+ {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true},
+ {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true},
+ {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true},
+ };
+
+ for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) {
+ {
+ expectedPresentTime += frameIntervalNs;
+ if (callExpectedPresent) {
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ frameIntervalNs))
+ .WillOnce(Return(Error::NONE));
+ } else {
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ }
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime),
+ Fps::fromPeriodNsecs(frameIntervalNs),
+ kTimeoutNs);
+ }
+ }
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8ba6bf8..16815be 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -303,13 +303,13 @@
auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
EXPECT_CALL(*vsyncTracker, minFramePeriod())
.WillRepeatedly(
Return(Period::fromNs(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread),
std::move(sfEventThread), DefaultDisplayMode{options.displayId},
SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp,
@@ -484,6 +484,10 @@
return mFlinger->setDisplayBrightness(display, brightness);
}
+ static const auto& getPendingMode(const sp<DisplayDevice>& display) {
+ return display->mPendingModeOpt;
+ }
+
// Allow reading display state without locking, as if called on the SF main thread.
auto setPowerModeInternal(const sp<DisplayDevice>& display,
hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
@@ -695,6 +699,21 @@
mFlinger->mLegacyFrontEndEnabled = false;
}
+ void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime, Fps frameInterval,
+ std::optional<Period> timeoutOpt) {
+ mFlinger->notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime,
+ frameInterval, timeoutOpt);
+ }
+
+ void setNotifyExpectedPresentData(PhysicalDisplayId displayId,
+ TimePoint lastExpectedPresentTimestamp,
+ Fps lastFrameInterval) {
+ auto& displayData = mFlinger->mNotifyExpectedPresentMap[displayId];
+ displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
+ displayData.lastFrameInterval = lastFrameInterval;
+ }
+
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the SurfaceFlinger instance may
diff --git a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
index 4a83d44..d071ce9 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
@@ -105,4 +105,16 @@
verifyTraceFile();
}
+// Check we cannot write to file if the trace write is disabled.
+TEST_F(TransactionTraceWriterTest, canDisableTraceWriter) {
+ TransactionTraceWriter::getInstance().disable();
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true);
+ EXPECT_NE(access(mFilename.c_str(), F_OK), 0);
+
+ TransactionTraceWriter::getInstance().enable();
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true);
+ EXPECT_EQ(access(mFilename.c_str(), F_OK), 0);
+ verifyTraceFile();
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 7981224..fb4ef70 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -214,6 +214,7 @@
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).layer_id(), mChildLayerId);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) {
@@ -224,6 +225,7 @@
perfetto::protos::TransactionTraceFile proto = writeToProto();
// verify starting states are updated correctly
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) {
@@ -235,6 +237,7 @@
// verify the child layer has been removed from the trace
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) {
@@ -254,6 +257,7 @@
// verify we still have the parent layer state
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 6a56353..d891008 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -64,7 +64,7 @@
public:
FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {}
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) const final {
auto const floor = timePoint % mPeriod;
if (floor == 0) {
return timePoint;
@@ -77,7 +77,7 @@
public:
VRRStubTracker(nsecs_t period) : StubTracker(period) {}
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) const final {
std::lock_guard lock(mMutex);
auto const normalized_to_base = time_point - mBase;
auto const floor = (normalized_to_base) % mPeriod;
@@ -117,7 +117,7 @@
mCallback.schedule(
{.workDuration = mWorkload,
.readyDuration = mReadyDuration,
- .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
+ .lastVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
for (auto i = 0u; i < iterations - 1; i++) {
std::unique_lock lock(mMutex);
@@ -130,7 +130,7 @@
mCallback.schedule({.workDuration = mWorkload,
.readyDuration = mReadyDuration,
- .earliestVsync = last + mWorkload + mReadyDuration});
+ .lastVsync = last + mWorkload + mReadyDuration});
}
// wait for the last callback.
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 2047018..4bf58de 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -46,14 +46,14 @@
class MockVSyncTracker : public mock::VSyncTracker {
public:
MockVSyncTracker(nsecs_t period) : mPeriod{period} {
- ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
+ ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_, _))
.WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
ON_CALL(*this, currentPeriod())
.WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod));
}
- nsecs_t nextVSyncTime(nsecs_t timePoint) const {
+ nsecs_t nextVSyncTime(nsecs_t timePoint, std::optional<nsecs_t>) const {
if (timePoint % mPeriod == 0) {
return timePoint;
}
@@ -243,10 +243,9 @@
mDispatchGroupThreshold,
mVsyncMoveThreshold);
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 1000});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -257,10 +256,9 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
@@ -277,16 +275,14 @@
EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
CountingCallback cb(mDispatch);
- auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended});
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
result =
- mDispatch->update(cb,
- {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+ mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(700, *result);
@@ -303,17 +299,18 @@
CountingCallback cb(mDispatch);
const auto result =
- mDispatch->update(cb,
- {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+ mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended});
EXPECT_FALSE(result.has_value());
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(mPeriod)))
+ .WillOnce(Return(1150));
EXPECT_CALL(mMockClock, alarmAt(_, 1050));
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -324,7 +321,8 @@
auto const now = 234;
mMockClock.advanceBy(234);
auto const workDuration = 10 * mPeriod;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + workDuration))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(now + workDuration, std::optional<nsecs_t>(mPeriod)))
.WillOnce(Return(mPeriod * 11));
EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
@@ -332,7 +330,7 @@
const auto result = mDispatch->schedule(cb,
{.workDuration = workDuration,
.readyDuration = 0,
- .earliestVsync = mPeriod});
+ .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod, *result);
}
@@ -342,10 +340,9 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled);
@@ -356,10 +353,9 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
mMockClock.advanceBy(950);
@@ -371,10 +367,9 @@
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
@@ -393,10 +388,9 @@
PausingCallback cb(mDispatch, 50ms);
cb.stashResource(resource);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
@@ -413,7 +407,8 @@
}
TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.Times(4)
.WillOnce(Return(1055))
.WillOnce(Return(1063))
@@ -428,8 +423,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
- mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
+ mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod});
advanceToNextCallback();
advanceToNextCallback();
@@ -441,7 +436,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(4)
.WillOnce(Return(1000))
.WillOnce(Return(2000))
@@ -455,21 +450,21 @@
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 0});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
EXPECT_THAT(cb.mCalls[0], Eq(1000));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(2));
EXPECT_THAT(cb.mCalls[1], Eq(2000));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
advanceToNextCallback();
@@ -478,7 +473,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(4)
.WillOnce(Return(10000))
.WillOnce(Return(1000))
@@ -493,9 +488,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
- mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod * 10});
+ mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod});
mDispatch->cancel(cb1);
}
@@ -507,9 +501,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
}
@@ -522,9 +516,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
}
@@ -542,10 +536,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1,
- {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = closeOffset, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb0.mCalls.size(), Eq(1));
@@ -553,11 +546,9 @@
ASSERT_THAT(cb1.mCalls.size(), Eq(1));
EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 2000});
mDispatch->schedule(cb1,
- {.workDuration = notCloseOffset,
- .readyDuration = 0,
- .earliestVsync = 2000});
+ {.workDuration = notCloseOffset, .readyDuration = 0, .lastVsync = 2000});
advanceToNextCallback();
ASSERT_THAT(cb1.mCalls.size(), Eq(2));
EXPECT_THAT(cb1.mCalls[1], Eq(2000));
@@ -577,32 +568,32 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
EXPECT_EQ(mDispatch->cancel(cb0), CancelResult::Cancelled);
}
TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(3)
.WillOnce(Return(950))
.WillOnce(Return(1975))
.WillOnce(Return(2950));
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 920});
mMockClock.advanceBy(850);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1900});
mMockClock.advanceBy(900);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
mMockClock.advanceBy(125);
EXPECT_THAT(cb.mCalls.size(), Eq(2));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2900});
mMockClock.advanceBy(975);
EXPECT_THAT(cb.mCalls.size(), Eq(3));
}
@@ -616,13 +607,11 @@
tmp = mDispatch->registerCallback(
[&](auto, auto, auto) {
mDispatch->schedule(tmp,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 2000});
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
},
"o.o");
- mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
}
@@ -631,30 +620,29 @@
std::optional<nsecs_t> lastTarget;
tmp = mDispatch->registerCallback(
[&](auto timestamp, auto, auto) {
- auto result =
- mDispatch->schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp - mVsyncMoveThreshold});
+ auto result = mDispatch->schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .lastVsync = timestamp - mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
result = mDispatch->schedule(tmp,
{.workDuration = 400,
.readyDuration = 0,
- .earliestVsync = timestamp});
+ .lastVsync = timestamp});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
result = mDispatch->schedule(tmp,
{.workDuration = 400,
.readyDuration = 0,
- .earliestVsync = timestamp + mVsyncMoveThreshold});
+ .lastVsync = timestamp + mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
lastTarget = timestamp;
},
"oo");
- mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
EXPECT_THAT(lastTarget, Eq(1000));
@@ -670,16 +658,16 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .lastVsync = 1000});
mMockClock.advanceBy(750);
- mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
- mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 2000});
mMockClock.advanceBy(800);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
}
TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
@@ -692,12 +680,12 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
- mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
- mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .lastVsync = 2000});
+ mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
advanceToNextCallback();
@@ -709,8 +697,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 20000});
}
TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
@@ -720,17 +708,15 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
CountingCallback cb0(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
mDispatch->cancel(cb0);
- mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
}
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
VSyncDispatch::CallbackToken token(100);
EXPECT_FALSE(
- mDispatch
- ->schedule(token,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+ mDispatch->schedule(token, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000})
.has_value());
EXPECT_THAT(mDispatch->cancel(token), Eq(CancelResult::Error));
}
@@ -738,12 +724,10 @@
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
auto result =
- mDispatch->schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
- result = mDispatch->schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -755,14 +739,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1200, *result);
@@ -779,14 +761,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 400)).InSequence(seq);
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(400, *result);
@@ -795,19 +775,18 @@
}
TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.Times(2)
.WillOnce(Return(1000))
.WillOnce(Return(1002));
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(602, *result);
}
@@ -815,13 +794,12 @@
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
CountingCallback cb0(mDispatch);
auto result =
- mDispatch->schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
advanceToNextCallback();
- result = mDispatch->schedule(cb0,
- {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+ result =
+ mDispatch->schedule(cb0, {.workDuration = 1100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -832,13 +810,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
auto result =
- mDispatch->schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
advanceToNextCallback();
- result = mDispatch->schedule(cb0,
- {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+ result =
+ mDispatch->schedule(cb0, {.workDuration = 1900, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1100, *result);
}
@@ -850,13 +827,11 @@
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb,
- {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
@@ -872,13 +847,11 @@
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb,
- {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(0, *result);
@@ -892,10 +865,10 @@
VSyncCallbackRegistration cb(
mDispatch, [](auto, auto, auto) {}, "");
VSyncCallbackRegistration cb1(std::move(cb));
- cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
cb.cancel();
- cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
cb1.cancel();
}
@@ -908,10 +881,10 @@
VSyncCallbackRegistration cb1(
mDispatch, [](auto, auto, auto) {}, "");
cb1 = std::move(cb);
- cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
cb.cancel();
- cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
cb1.cancel();
}
@@ -924,16 +897,14 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- result = mDispatch->schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
mMockClock.advanceBy(80);
@@ -952,16 +923,14 @@
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- result = mDispatch->schedule(cb,
- {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1630, *result);
mMockClock.advanceBy(80);
@@ -978,12 +947,10 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
@@ -1007,12 +974,10 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
@@ -1034,21 +999,21 @@
CountingCallback cb2(mDispatch);
Sequence seq;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.InSequence(seq)
.WillOnce(Return(1000));
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.InSequence(seq)
.WillOnce(Return(1000));
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb2,
- {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb2, {.workDuration = 390, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(610, *result);
@@ -1070,10 +1035,9 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 70,
- .readyDuration = 30,
- .earliestVsync = intended});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 70, .readyDuration = 30, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
advanceToNextCallback();
@@ -1094,8 +1058,8 @@
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
@@ -1118,8 +1082,8 @@
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
@@ -1139,14 +1103,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(300);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1200, *result);
@@ -1162,14 +1124,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 300)).InSequence(seq);
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(300);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(300, *result);
@@ -1204,7 +1164,7 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
@@ -1219,14 +1179,15 @@
auto const duration = 500;
auto const now = 8750;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + duration))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(now + duration, std::optional<nsecs_t>(994)))
.Times(1)
.WillOnce(Return(10000));
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+ EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
*mStubTracker.get(), now)
.has_value());
auto const wakeup = entry.wakeupTime();
@@ -1249,7 +1210,7 @@
},
mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
@@ -1272,7 +1233,7 @@
}
TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(2)
.WillOnce(Return(1000))
.WillOnce(Return(1020));
@@ -1284,7 +1245,7 @@
entry.update(*mStubTracker.get(), 0);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto wakeup = entry.wakeupTime();
@@ -1300,7 +1261,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
entry.update(*mStubTracker.get(), 0);
@@ -1313,24 +1274,24 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
*mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
@@ -1343,23 +1304,25 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
Sequence seq;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500)))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500)))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold,
+ std::optional<nsecs_t>(1000)))
.InSequence(seq)
.WillOnce(Return(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
entry.executing(); // 1000 is executing
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
}
@@ -1367,16 +1330,16 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
}
@@ -1386,9 +1349,9 @@
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
- entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400});
+ entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .lastVsync = 400});
entry.addPendingWorkloadUpdate(
- {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
+ {.workDuration = effectualOffset, .readyDuration = 0, .lastVsync = 400});
EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
entry.update(*mStubTracker.get(), 0);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
@@ -1410,7 +1373,7 @@
},
mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 7a498c9..9afaabe 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -720,15 +720,17 @@
vrrTracker.setRenderRate(minFrameRate);
vrrTracker.addVsyncTimestamp(0);
EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
- EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1300));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
- EXPECT_EQ(1500, vrrTracker.nextAnticipatedVSyncTimeFrom(1300));
- EXPECT_EQ(2500, vrrTracker.nextAnticipatedVSyncTimeFrom(2300));
+ EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 2000));
+ EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(3500, 3500));
- vrrTracker.onFrameMissed(TimePoint::fromNs(2500));
- EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2300));
- EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3300));
+ // Miss when starting 4500 and expect the next vsync will be at 5000 (next one)
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3500), TimePoint::fromNs(2500));
+ vrrTracker.onFrameMissed(TimePoint::fromNs(4500));
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index d649679..184dada 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -51,7 +51,7 @@
~Composer() override;
MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
- MOCK_METHOD(bool, getDisplayConfigurationsSupported, (), (const, override));
+ MOCK_METHOD(bool, isVrrSupported, (), (const, override));
MOCK_METHOD0(getCapabilities,
std::vector<aidl::android::hardware::graphics::composer3::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
index a088aab..ed1405b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -18,12 +18,21 @@
#include "binder/Status.h"
+// FMQ library in IPower does questionable conversions
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
+#pragma clang diagnostic pop
+
#include <gmock/gmock.h>
using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::ChannelConfig;
using aidl::android::hardware::power::IPower;
using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionConfig;
+using aidl::android::hardware::power::SessionTag;
+
using aidl::android::hardware::power::Mode;
using android::binder::Status;
@@ -42,6 +51,14 @@
int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, SessionTag tag, SessionConfig* config,
+ std::shared_ptr<IPowerHintSession>* _aidl_return),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
+ (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index 364618d..27564b2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -18,10 +18,15 @@
#include "binder/Status.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
+#pragma clang diagnostic pop
+
#include <gmock/gmock.h>
using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionConfig;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::SessionMode;
using android::binder::Status;
@@ -47,6 +52,7 @@
MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSessionConfig, (SessionConfig * _aidl_return), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index 68fe3c5..b17c8ad 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -19,7 +19,10 @@
#include <gmock/gmock.h>
#include <scheduler/Time.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <powermanager/PowerHalController.h>
+#pragma clang diagnostic pop
namespace android {
namespace hardware {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 866af3b..e2b0ed1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -59,6 +59,9 @@
MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
MOCK_METHOD(void, pauseVsyncCallback, (bool));
MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override));
+ MOCK_METHOD(void, onHdcpLevelsChanged,
+ (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel),
+ (override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 3dfb649..4204aa0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -17,6 +17,7 @@
#pragma once
#include <gmock/gmock.h>
+#include <optional>
namespace android::mock {
@@ -27,6 +28,13 @@
EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
.WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
}
+
+ MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {
+ EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
+ .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
+ }
+
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
MOCK_CONST_METHOD0(getType, const char*());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index e588bb9..3870983 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -28,7 +28,8 @@
~VSyncTracker() override;
MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override));
- MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t), (const, override));
+ MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t, std::optional<nsecs_t>),
+ (const, override));
MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override));
MOCK_METHOD(Period, minFramePeriod, (), (const, override));
MOCK_METHOD(void, resetModel, (), (override));
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 7c8e695..159b2d5 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -63,7 +63,8 @@
/*
* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
*
- * This version of the extension deprecates the last of grallocusage
+ * This version of the extension deprecates the last of grallocusage and
+ * extends VkNativeBufferANDROID to support passing AHardwareBuffer*
*/
#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
@@ -111,6 +112,9 @@
* usage: gralloc usage requested when the buffer was allocated
* usage2: gralloc usage requested when the buffer was allocated
* usage3: gralloc usage requested when the buffer was allocated
+ * ahb: The AHardwareBuffer* from the actual ANativeWindowBuffer. Caller
+ * maintains ownership of resource. AHardwareBuffer pointer is only valid
+ * for the duration of the function call
*/
typedef struct {
VkStructureType sType;
@@ -121,6 +125,7 @@
int usage; /* DEPRECATED in SPEC_VERSION 6 */
VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */
uint64_t usage3; /* ADDED in SPEC_VERSION 9 */
+ struct AHardwareBuffer* ahb; /* ADDED in SPEC_VERSION 11 */
} VkNativeBufferANDROID;
/*
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 4f7b0f4..0df5e77 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1970,6 +1970,8 @@
&image_native_buffer.usage2.producer,
&image_native_buffer.usage2.consumer);
image_native_buffer.usage3 = img.buffer->usage;
+ image_native_buffer.ahb =
+ ANativeWindowBuffer_getHardwareBuffer(img.buffer.get());
image_create.pNext = &image_native_buffer;
ATRACE_BEGIN("CreateImage");
@@ -2146,7 +2148,12 @@
.stride = buffer->stride,
.format = buffer->format,
.usage = int(buffer->usage),
+ .usage3 = buffer->usage,
+ .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer),
};
+ android_convertGralloc0To1Usage(int(buffer->usage),
+ &nb.usage2.producer,
+ &nb.usage2.consumer);
VkBindImageMemoryInfo bimi = {
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
.pNext = &nb,
@@ -2692,7 +2699,12 @@
.stride = buffer->stride,
.format = buffer->format,
.usage = int(buffer->usage),
+ .usage3 = buffer->usage,
+ .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer),
};
+ android_convertGralloc0To1Usage(int(buffer->usage),
+ &native_buffer.usage2.producer,
+ &native_buffer.usage2.consumer);
// Reserve enough space to avoid letting re-allocation invalidate the
// addresses of the elements inside.
out_native_buffers->reserve(bind_info_count);
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
index a6d540b..5112e14 100644
--- a/vulkan/nulldrv/Android.bp
+++ b/vulkan/nulldrv/Android.bp
@@ -46,5 +46,8 @@
"hwvulkan_headers",
"vulkan_headers",
],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "liblog",
+ "libnativewindow",
+ ],
}
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 2e87f17..973e71c 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/hardware_buffer.h>
#include <hardware/hwvulkan.h>
#include <errno.h>
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 935535f..d34851e 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -16,6 +16,8 @@
// WARNING: This file is generated. See ../README.md for instructions.
+#include <android/hardware_buffer.h>
+
#include <algorithm>
#include "null_driver_gen.h"