Merge "Add debug info to crash in SkiaGLRenderEngine::chooseEglConfig" into main
diff --git a/cmds/ip-up-vpn/Android.bp b/cmds/ip-up-vpn/Android.bp
deleted file mode 100644
index c746f7f..0000000
--- a/cmds/ip-up-vpn/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
- name: "ip-up-vpn",
-
- srcs: ["ip-up-vpn.c"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: [
- "libcutils",
- "liblog",
- ],
-}
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
deleted file mode 100644
index 71f0837..0000000
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ip-up-vpn"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <linux/if.h>
-#include <linux/route.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <log/log.h>
-
-#define DIR "/data/misc/vpn/"
-
-static const char *env(const char *name) {
- const char *value = getenv(name);
- return value ? value : "";
-}
-
-static int set_address(struct sockaddr *sa, const char *address) {
- sa->sa_family = AF_INET;
- errno = EINVAL;
- return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr);
-}
-
-/*
- * The primary goal is to create a file with VPN parameters. Currently they
- * are interface, addresses, routes, DNS servers, and search domains and VPN
- * server address. Each parameter occupies one line in the file, and it can be
- * an empty string or space-separated values. The order and the format must be
- * consistent with com.android.server.connectivity.Vpn. Here is an example.
- *
- * ppp0
- * 192.168.1.100/24
- * 0.0.0.0/0
- * 192.168.1.1 192.168.1.2
- * example.org
- * 192.0.2.1
- *
- * The secondary goal is to unify the outcome of VPN. The current baseline
- * is to have an interface configured with the given address and netmask
- * and maybe add a host route to protect the tunnel. PPP-based VPN already
- * does this, but others might not. Routes, DNS servers, and search domains
- * are handled by the framework since they can be overridden by the users.
- */
-int main(int argc, char **argv)
-{
- FILE *state = fopen(DIR ".tmp", "wb");
- if (!state) {
- ALOGE("Cannot create state: %s", strerror(errno));
- return 1;
- }
-
- if (argc >= 6) {
- /* Invoked by pppd. */
- fprintf(state, "%s\n", argv[1]);
- fprintf(state, "%s/32\n", argv[4]);
- fprintf(state, "0.0.0.0/0\n");
- fprintf(state, "%s %s\n", env("DNS1"), env("DNS2"));
- fprintf(state, "\n");
- fprintf(state, "\n");
- } else if (argc == 2) {
- /* Invoked by racoon. */
- const char *interface = env("INTERFACE");
- const char *address = env("INTERNAL_ADDR4");
- const char *routes = env("SPLIT_INCLUDE_CIDR");
-
- int s = socket(AF_INET, SOCK_DGRAM, 0);
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
-
- /* Bring up the interface. */
- ifr.ifr_flags = IFF_UP;
- strncpy(ifr.ifr_name, interface, IFNAMSIZ);
- if (ioctl(s, SIOCSIFFLAGS, &ifr)) {
- ALOGE("Cannot bring up %s: %s", interface, strerror(errno));
- fclose(state);
- return 1;
- }
-
- /* Set the address. */
- if (!set_address(&ifr.ifr_addr, address) ||
- ioctl(s, SIOCSIFADDR, &ifr)) {
- ALOGE("Cannot set address: %s", strerror(errno));
- fclose(state);
- return 1;
- }
-
- /* Set the netmask. */
- if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) {
- if (ioctl(s, SIOCSIFNETMASK, &ifr)) {
- ALOGE("Cannot set netmask: %s", strerror(errno));
- fclose(state);
- return 1;
- }
- }
-
- /* TODO: Send few packets to trigger phase 2? */
-
- fprintf(state, "%s\n", interface);
- fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4"));
- fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0");
- fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST"));
- fprintf(state, "%s\n", env("DEFAULT_DOMAIN"));
- fprintf(state, "%s\n", env("REMOTE_ADDR"));
- } else {
- ALOGE("Cannot parse parameters");
- fclose(state);
- return 1;
- }
-
- fclose(state);
- if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) {
- ALOGE("Cannot write state: %s", strerror(errno));
- return 1;
- }
- return 0;
-}
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/MotionPredictor.h b/include/input/MotionPredictor.h
index 8797962..3b6e401 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
#include <mutex>
+#include <optional>
#include <string>
#include <unordered_map>
@@ -57,20 +58,23 @@
*/
class MotionPredictor {
public:
+ using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction;
+
/**
* Parameters:
* predictionTimestampOffsetNanos: additional, constant shift to apply to the target
* prediction time. The prediction will target the time t=(prediction time +
* predictionTimestampOffsetNanos).
*
- * modelPath: filesystem path to a TfLiteMotionPredictorModel flatbuffer, or nullptr to use the
- * default model path.
- *
- * checkEnableMotionPredition: the function to check whether the prediction should run. Used to
+ * checkEnableMotionPrediction: the function to check whether the prediction should run. Used to
* provide an additional way of turning prediction on and off. Can be toggled at runtime.
+ *
+ * reportAtomFunction: the function that will be called to report prediction metrics. If
+ * omitted, the implementation will choose a default metrics reporting mechanism.
*/
MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
- std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);
+ std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled,
+ ReportAtomFunction reportAtomFunction = {});
/**
* Record the actual motion received by the view. This event will be used for calculating the
@@ -95,6 +99,8 @@
std::optional<MotionEvent> mLastEvent;
std::optional<MotionPredictorMetricsManager> mMetricsManager;
+
+ const ReportAtomFunction mReportAtomFunction;
};
} // namespace android
diff --git a/include/input/MotionPredictorMetricsManager.h b/include/input/MotionPredictorMetricsManager.h
index 12e50ba..38472d8 100644
--- a/include/input/MotionPredictorMetricsManager.h
+++ b/include/input/MotionPredictorMetricsManager.h
@@ -18,7 +18,6 @@
#include <cstdint>
#include <functional>
#include <limits>
-#include <optional>
#include <vector>
#include <input/Input.h> // for MotionEvent
@@ -37,15 +36,33 @@
*
* This class stores AggregatedStrokeMetrics, updating them as new MotionEvents are passed in. When
* onRecord receives an UP or CANCEL event, this indicates the end of the stroke, and the final
- * AtomFields are computed and reported to the stats library.
+ * AtomFields are computed and reported to the stats library. The number of atoms reported is equal
+ * to the value of `maxNumPredictions` passed to the constructor. Each atom corresponds to one
+ * "prediction time bucket" — the amount of time into the future being predicted.
*
* If mMockLoggedAtomFields is set, the batch of AtomFields that are reported to the stats library
* for one stroke are also stored in mMockLoggedAtomFields at the time they're reported.
*/
class MotionPredictorMetricsManager {
public:
- // Note: the MetricsManager assumes that the input interval equals the prediction interval.
- MotionPredictorMetricsManager(nsecs_t predictionInterval, size_t maxNumPredictions);
+ struct AtomFields;
+
+ using ReportAtomFunction = std::function<void(const AtomFields&)>;
+
+ static void defaultReportAtomFunction(const AtomFields& atomFields);
+
+ // Parameters:
+ // • predictionInterval: the time interval between successive prediction target timestamps.
+ // Note: the MetricsManager assumes that the input interval equals the prediction interval.
+ // • maxNumPredictions: the maximum number of distinct target timestamps the prediction model
+ // will generate predictions for. The MetricsManager reports this many atoms per stroke.
+ // • [Optional] reportAtomFunction: the function that will be called to report metrics. If
+ // omitted (or if an empty function is given), the `stats_write(…)` function from the Android
+ // stats library will be used.
+ MotionPredictorMetricsManager(
+ nsecs_t predictionInterval,
+ size_t maxNumPredictions,
+ ReportAtomFunction reportAtomFunction = defaultReportAtomFunction);
// This method should be called once for each call to MotionPredictor::record, receiving the
// forwarded MotionEvent argument.
@@ -121,7 +138,7 @@
// magnitude makes it unobtainable in practice.)
static const int NO_DATA_SENTINEL = std::numeric_limits<int32_t>::min();
- // Final metrics reported in the atom.
+ // Final metric values reported in the atom.
struct AtomFields {
int deltaTimeBucketMilliseconds = 0;
@@ -140,15 +157,6 @@
int scaleInvariantOffTrajectoryRmse = NO_DATA_SENTINEL; // millipixels
};
- // Allow tests to pass in a mock AtomFields pointer.
- //
- // When metrics are reported to the stats library on stroke end, they will also be written to
- // mockLoggedAtomFields, overwriting existing data. The size of mockLoggedAtomFields will equal
- // the number of calls to stats_write for that stroke.
- void setMockLoggedAtomFields(std::vector<AtomFields>* mockLoggedAtomFields) {
- mMockLoggedAtomFields = mockLoggedAtomFields;
- }
-
private:
// The interval between consecutive predictions' target timestamps. We assume that the input
// interval also equals this value.
@@ -172,11 +180,7 @@
std::vector<AggregatedStrokeMetrics> mAggregatedMetrics;
std::vector<AtomFields> mAtomFields;
- // Non-owning pointer to the location of mock AtomFields. If present, will be filled with the
- // values reported to stats_write on each batch of reported metrics.
- //
- // This pointer must remain valid as long as the MotionPredictorMetricsManager exists.
- std::vector<AtomFields>* mMockLoggedAtomFields = nullptr;
+ const ReportAtomFunction mReportAtomFunction;
// Helper methods for the implementation of onRecord and onPredict.
@@ -196,10 +200,7 @@
// Computes the atom fields to mAtomFields from the values in mAggregatedMetrics.
void computeAtomFields();
- // Reports the metrics given by the current data in mAtomFields:
- // • If on an Android device, reports the metrics to stats_write.
- // • If mMockLoggedAtomFields is present, it will be overwritten with logged metrics, with one
- // AtomFields element per call to stats_write.
+ // Reports the current data in mAtomFields by calling mReportAtomFunction.
void reportMetrics();
};
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 63c0e40..83fffa3 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);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 11e8120..bf3699c 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,19 @@
"file.cpp",
],
- shared_libs: [
- "libcutils",
- "libutils",
- ],
-
- static_libs: [
- "libbase",
- ],
-
header_libs: [
- "libbinder_headers",
+ "libbinder_headers_base",
+ ],
+
+ cflags: [
+ "-Wextra",
+ "-Wextra-semi",
+ "-Werror",
+ "-Wzero-as-null-pointer-constant",
+ "-Wreorder-init-list",
+ "-Wunused-const-variable",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
}
@@ -120,7 +151,6 @@
srcs: [
"OS_android.cpp",
"OS_unix_base.cpp",
- "RpcTransportRaw.cpp",
],
target: {
@@ -135,15 +165,6 @@
export_aidl_headers: true,
},
- cflags: [
- "-Wextra",
- "-Wextra-semi",
- "-Werror",
- "-Wzero-as-null-pointer-constant",
- "-Wreorder-init-list",
- "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
- "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
- ],
product_variables: {
debuggable: {
cflags: [
@@ -154,11 +175,18 @@
},
shared_libs: [
+ "libcutils",
"liblog",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
],
header_libs: [
"jni_headers",
+ "libbinder_headers",
],
export_header_lib_headers: [
@@ -215,11 +243,21 @@
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",
// Trusty libbinder uses vendor stability for its binders
"-D__ANDROID_VNDK__",
"-U__ANDROID__",
@@ -256,9 +294,6 @@
"trusty/TrustyStatus.cpp",
"trusty/socket.cpp",
],
- shared_libs: [
- "liblog",
- ],
}
cc_defaults {
@@ -347,6 +382,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/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/Parcel.cpp b/libs/binder/Parcel.cpp
index 146ddef..c1770b3 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -114,7 +114,7 @@
constexpr size_t kMaxFds = 1024;
// Maximum size of a blob to transfer in-place.
-static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
+[[maybe_unused]] static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
#if defined(__BIONIC__)
static void FdTag(int fd, const void* old_addr, const void* new_addr) {
@@ -886,6 +886,9 @@
}
#ifdef BINDER_WITH_KERNEL_IPC
+
+#if defined(__ANDROID__)
+
#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#elif defined(__ANDROID_RECOVERY__)
@@ -893,6 +896,14 @@
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
#endif
+
+#else // ANDROID not defined
+
+// If kernel binder is used in new environments, we need to make sure it's separated
+// out and has a separate header.
+constexpr int32_t kHeader = B_PACK_CHARS('U', 'N', 'K', 'N');
+#endif
+
#endif // BINDER_WITH_KERNEL_IPC
// Write RPC headers. (previously just the interface token)
diff --git a/libs/binder/file.cpp b/libs/binder/file.cpp
index bac667e..6e450b9 100644
--- a/libs/binder/file.cpp
+++ b/libs/binder/file.cpp
@@ -18,6 +18,8 @@
#ifdef BINDER_NO_LIBBASE
+#include "Utils.h"
+
#include <stdint.h>
// clang-format off
diff --git a/libs/binder/include/binder/Delegate.h b/libs/binder/include/binder/Delegate.h
index 8b3fc1c..ad5a6a3 100644
--- a/libs/binder/include/binder/Delegate.h
+++ b/libs/binder/include/binder/Delegate.h
@@ -18,6 +18,11 @@
#include <binder/IBinder.h>
+#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT)
+#include <log/log.h>
+#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message)
+#endif
+
#ifndef __BIONIC__
#ifndef __assert
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/Android.bp b/libs/binder/liblog_stub/Android.bp
new file mode 100644
index 0000000..f2ca22f
--- /dev/null
+++ b/libs/binder/liblog_stub/Android.bp
@@ -0,0 +1,44 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_headers {
+ name: "liblog_stub",
+ export_include_dirs: ["include"],
+
+ host_supported: true,
+ native_bridge_supported: true,
+ product_available: true,
+ recovery_available: true,
+ vendor_available: true,
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ visibility: [
+ "//frameworks/native/libs/binder:__subpackages__",
+ "//system/core/libutils/binder",
+ ],
+}
diff --git a/libs/binder/liblog_stub/include/android/log.h b/libs/binder/liblog_stub/include/android/log.h
new file mode 100644
index 0000000..9dcd926
--- /dev/null
+++ b/libs/binder/liblog_stub/include/android/log.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+extern "C" {
+
+/**
+ * Android log priority values, in increasing order of priority.
+ */
+typedef enum android_LogPriority {
+ /** For internal use only. */
+ ANDROID_LOG_UNKNOWN = 0,
+ /** The default priority, for internal use only. */
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ /** Verbose logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_VERBOSE,
+ /** Debug logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_DEBUG,
+ /** Informational logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_INFO,
+ /** Warning logging. For use with recoverable failures. */
+ ANDROID_LOG_WARN,
+ /** Error logging. For use with unrecoverable failures. */
+ ANDROID_LOG_ERROR,
+ /** Fatal logging. For use when aborting. */
+ ANDROID_LOG_FATAL,
+ /** For internal use only. */
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+typedef void (*__android_logger_function)(const struct __android_log_message* log_message);
+inline void __android_log_set_logger(__android_logger_function) {}
+inline void __android_log_stderr_logger(const struct __android_log_message*) {}
+
+} // extern "C"
diff --git a/libs/binder/liblog_stub/include/log/log.h b/libs/binder/liblog_stub/include/log/log.h
new file mode 100644
index 0000000..91c9632
--- /dev/null
+++ b/libs/binder/liblog_stub/include/log/log.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <android/log.h>
+
+extern "C" {
+
+#ifndef ANDROID_LOG_STUB_MIN_PRIORITY
+#define ANDROID_LOG_STUB_MIN_PRIORITY ANDROID_LOG_INFO
+#endif
+
+#ifndef LOG_TAG
+#define LOG_TAG ""
+#endif
+
+constexpr bool __android_log_stub_is_loggable(android_LogPriority priority) {
+ return ANDROID_LOG_STUB_MIN_PRIORITY <= priority;
+}
+
+#ifdef ANDROID_LOG_STUB_WEAK_PRINT
+#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_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)
+#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
+#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
+
+#define ALOG(priority, tag, fmt, ...) \
+ do { \
+ if (false)[[/*VERY*/ unlikely]] { /* ignore unused __VA_ARGS__ */ \
+ std::fprintf(stderr, fmt __VA_OPT__(, ) __VA_ARGS__); \
+ } \
+ IF_ALOG(priority, tag) { \
+ __android_log_print(ANDROID_##priority, tag, \
+ tag ": " fmt "\n" __VA_OPT__(, ) __VA_ARGS__); \
+ } \
+ if constexpr (ANDROID_##priority == ANDROID_LOG_FATAL) std::abort(); \
+ } while (false)
+#define ALOGV(...) ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define LOG_FATAL(...) ALOG(LOG_FATAL, LOG_TAG, __VA_ARGS__)
+#define LOG_ALWAYS_FATAL LOG_FATAL
+
+#define ALOG_IF(cond, priority, tag, ...) \
+ if (cond) [[unlikely]] \
+ ALOG(priority, tag, #cond ": " __VA_ARGS__)
+#define ALOGV_IF(cond, ...) ALOG_IF(cond, LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define ALOGD_IF(cond, ...) ALOG_IF(cond, LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGI_IF(cond, ...) ALOG_IF(cond, LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW_IF(cond, ...) ALOG_IF(cond, LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE_IF(cond, ...) ALOG_IF(cond, LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define LOG_FATAL_IF(cond, ...) ALOG_IF(cond, LOG_FATAL, LOG_TAG, __VA_ARGS__)
+#define LOG_ALWAYS_FATAL_IF LOG_FATAL_IF
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
+
+inline int android_errorWriteLog(int tag, const char* subTag) {
+ ALOGE("android_errorWriteLog(%x, %s)", tag, subTag);
+ return 0;
+}
+
+} // extern "C"
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/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 4786c89..14edf2b 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -31,6 +31,11 @@
#include <stdint.h>
#include <sys/cdefs.h>
+#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT)
+#include <log/log.h>
+#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message)
+#endif
+
__BEGIN_DECLS
#ifndef __BIONIC__
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index c1f62e5..089c775 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -21,17 +21,15 @@
__BEGIN_DECLS
/**
- * Private addition to binder_flag_t.
+ * Indicates that this transaction is coupled w/ vendor.img
*/
-enum {
- /**
- * Indicates that this transaction is coupled w/ vendor.img
- */
- FLAG_PRIVATE_VENDOR = 0x10000000,
-};
+constexpr binder_flags_t FLAG_PRIVATE_VENDOR = 0x10000000;
#if defined(__ANDROID_VENDOR__)
+/**
+ * Private addition to binder_flag_t.
+ */
enum {
FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR,
};
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/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index a55edd2..62fe9e5 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -393,7 +393,7 @@
}
if (delayed) {
- RpcMaybeThread([=]() {
+ RpcMaybeThread([=, this]() {
ALOGE("Executing delayed callback: '%s'", value.c_str());
Status status = doCallback(callback, oneway, false, value);
ALOGE("Delayed callback status: '%s'", status.toString8().c_str());
diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
index aaca8d0..8346b36 100644
--- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
@@ -77,16 +77,14 @@
// Message size needs to be large enough to cover all messages sent by the
// tests: SendAndGetResultBackBig sends two large strings.
constexpr size_t max_msg_size = 4096;
- auto serverOrErr =
+ auto server =
RpcServerTrusty::make(hset, serverInfo.port->c_str(),
std::shared_ptr<const RpcServerTrusty::PortAcl>(&port_acl),
max_msg_size);
- if (!serverOrErr.ok()) {
- TLOGE("Failed to create RpcServer (%d)\n", serverOrErr.error());
+ if (server == nullptr) {
return EXIT_FAILURE;
}
- auto server = std::move(*serverOrErr);
serverInfo.server = server;
if (!serverInfo.server->setProtocolVersion(serverVersion)) {
return EXIT_FAILURE;
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 99da1eb..a8dabc3 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -22,10 +22,14 @@
#endif
#include <binder/RpcTransportTipcTrusty.h>
+#include <log/log.h>
+#include <trusty_log.h>
#include "../OS.h"
#include "TrustyStatus.h"
+#include <cstdarg>
+
using android::binder::borrowed_fd;
using android::binder::unique_fd;
@@ -87,3 +91,38 @@
}
} // namespace android::binder::os
+
+int __android_log_print(int prio [[maybe_unused]], const char* tag, const char* fmt, ...) {
+#ifdef TRUSTY_USERSPACE
+#define trusty_tlog _tlog
+#define trusty_vtlog _vtlog
+#else
+ // mapping taken from kernel trusty_log.h (TLOGx)
+ int kernelLogLevel;
+ if (prio <= ANDROID_LOG_DEBUG) {
+ kernelLogLevel = LK_DEBUGLEVEL_ALWAYS;
+ } else if (prio == ANDROID_LOG_INFO) {
+ kernelLogLevel = LK_DEBUGLEVEL_SPEW;
+ } else if (prio == ANDROID_LOG_WARN) {
+ kernelLogLevel = LK_DEBUGLEVEL_INFO;
+ } else if (prio == ANDROID_LOG_ERROR) {
+ kernelLogLevel = LK_DEBUGLEVEL_CRITICAL;
+ } else { /* prio >= ANDROID_LOG_FATAL */
+ kernelLogLevel = LK_DEBUGLEVEL_CRITICAL;
+ }
+#if LK_DEBUGLEVEL_NO_ALIASES
+ auto LK_DEBUGLEVEL_kernelLogLevel = kernelLogLevel;
+#endif
+
+#define trusty_tlog(...) _tlog(kernelLogLevel, __VA_ARGS__)
+#define trusty_vtlog(...) _vtlog(kernelLogLevel, __VA_ARGS__)
+#endif
+
+ va_list args;
+ va_start(args, fmt);
+ trusty_tlog((tag[0] == '\0') ? "libbinder" : "libbinder-");
+ trusty_vtlog(fmt, args);
+ va_end(args);
+
+ return 1;
+}
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 0872b90..1f857a0 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -27,12 +27,11 @@
#include "../RpcState.h"
#include "TrustyStatus.h"
-using android::base::unexpected;
using android::binder::unique_fd;
namespace android {
-android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make(
+sp<RpcServerTrusty> RpcServerTrusty::make(
tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
// Default is without TLS.
@@ -40,18 +39,21 @@
rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make();
auto ctx = rpcTransportCtxFactory->newServerCtx();
if (ctx == nullptr) {
- return unexpected(ERR_NO_MEMORY);
+ ALOGE("Failed to create RpcServerTrusty: can't create server context");
+ return nullptr;
}
auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl),
msgMaxSize);
if (srv == nullptr) {
- return unexpected(ERR_NO_MEMORY);
+ ALOGE("Failed to create RpcServerTrusty: can't create server object");
+ return nullptr;
}
int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps);
if (rc != NO_ERROR) {
- return unexpected(rc);
+ ALOGE("Failed to create RpcServerTrusty: can't add service: %d", rc);
+ return nullptr;
}
return srv;
}
diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json
index 1c4f7ee..d2a1fc0 100644
--- a/libs/binder/trusty/binderRpcTest/service/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/service/manifest.json
@@ -2,7 +2,7 @@
"uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94",
"app_name": "binderRpcTestService",
"min_heap": 65536,
- "min_stack": 16384,
+ "min_stack": 20480,
"mgmt_flags": {
"restart_on_exit": true,
"non_critical_app": true
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/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 7382a30..f35d6c2 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -16,7 +16,6 @@
#pragma once
-#include <android-base/expected.h>
#include <binder/IBinder.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
@@ -53,7 +52,7 @@
* The caller is responsible for calling tipc_run_event_loop() to start
* the TIPC event loop after creating one or more services here.
*/
- static android::base::expected<sp<RpcServerTrusty>, int> make(
+ static sp<RpcServerTrusty> make(
tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
size_t msgMaxSize,
std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
deleted file mode 100644
index de84617..0000000
--- a/libs/binder/trusty/include/log/log.h
+++ /dev/null
@@ -1,128 +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.
- */
-
-#pragma once
-
-#define BINDER_LOG_LEVEL_NONE 0
-#define BINDER_LOG_LEVEL_NORMAL 1
-#define BINDER_LOG_LEVEL_VERBOSE 2
-
-#ifndef BINDER_LOG_LEVEL
-#define BINDER_LOG_LEVEL BINDER_LOG_LEVEL_NORMAL
-#endif // BINDER_LOG_LEVEL
-
-#ifndef TLOG_TAG
-#ifdef LOG_TAG
-#define TLOG_TAG "libbinder-" LOG_TAG
-#else // LOG_TAG
-#define TLOG_TAG "libbinder"
-#endif // LOG_TAG
-#endif // TLOG_TAG
-
-#include <stdlib.h>
-#include <trusty_log.h>
-
-static inline void __ignore_va_args__(...) {}
-
-#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
-#define ALOGD(fmt, ...) TLOGD(fmt "\n", ##__VA_ARGS__)
-#define ALOGI(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
-#define ALOGW(fmt, ...) TLOGW(fmt "\n", ##__VA_ARGS__)
-#define ALOGE(fmt, ...) TLOGE(fmt "\n", ##__VA_ARGS__)
-#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
-#define ALOGD(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#define ALOGI(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#define ALOGW(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#define ALOGE(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
-
-#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
-#define IF_ALOGV() if (TLOG_LVL >= TLOG_LVL_INFO)
-#define ALOGV(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
-#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
-#define IF_ALOGV() if (false)
-#define ALOGV(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
-
-#define ALOGI_IF(cond, ...) \
- do { \
- if (cond) { \
- ALOGI(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-#define ALOGE_IF(cond, ...) \
- do { \
- if (cond) { \
- ALOGE(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-#define ALOGW_IF(cond, ...) \
- do { \
- if (cond) { \
- ALOGW(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-
-#define LOG_ALWAYS_FATAL(fmt, ...) \
- do { \
- TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
- abort(); \
- } while (0)
-#define LOG_ALWAYS_FATAL_IF(cond, ...) \
- do { \
- if (cond) { \
- LOG_ALWAYS_FATAL(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-#define LOG_FATAL(fmt, ...) \
- do { \
- TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
- abort(); \
- } while (0)
-#define LOG_FATAL_IF(cond, ...) \
- do { \
- if (cond) { \
- LOG_FATAL(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-
-#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
-
-#define android_errorWriteLog(tag, subTag) \
- do { \
- TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
- } while (0)
-
-// Override the definition of __assert from binder_status.h
-#ifndef __BIONIC__
-#undef __assert
-#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
-#endif // __BIONIC__
diff --git a/libs/binder/trusty/include_mock/trusty_log.h b/libs/binder/trusty/include_mock/trusty_log.h
index d51e752..9aa9031 100644
--- a/libs/binder/trusty/include_mock/trusty_log.h
+++ b/libs/binder/trusty/include_mock/trusty_log.h
@@ -24,3 +24,6 @@
#define TLOGW(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define TLOGE(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define TLOGC(fmt, ...) printf(fmt, ##__VA_ARGS__)
+
+#define _tlog(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#define _vtlog(fmt, args) vprintf(fmt, args)
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 69737fa..788184d 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -18,9 +18,10 @@
MODULE := $(LOCAL_DIR)
LIBBINDER_DIR := frameworks/native/libs/binder
+# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release
LIBBASE_DIR := system/libbase
-LIBCUTILS_DIR := system/core/libcutils
-LIBUTILS_DIR := system/core/libutils
+LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub
+LIBUTILS_BINDER_DIR := system/core/libutils/binder
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
@@ -35,17 +36,14 @@
$(LIBBINDER_DIR)/Stability.cpp \
$(LIBBINDER_DIR)/Status.cpp \
$(LIBBINDER_DIR)/Utils.cpp \
- $(LIBBASE_DIR)/hex.cpp \
- $(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/binder/Errors.cpp \
- $(LIBUTILS_DIR)/binder/RefBase.cpp \
- $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/binder/String16.cpp \
- $(LIBUTILS_DIR)/binder/String8.cpp \
- $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
- $(LIBUTILS_DIR)/binder/Unicode.cpp \
- $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
- $(LIBUTILS_DIR)/misc.cpp \
+ $(LIBUTILS_BINDER_DIR)/Errors.cpp \
+ $(LIBUTILS_BINDER_DIR)/RefBase.cpp \
+ $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \
+ $(LIBUTILS_BINDER_DIR)/String16.cpp \
+ $(LIBUTILS_BINDER_DIR)/String8.cpp \
+ $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \
+ $(LIBUTILS_BINDER_DIR)/Unicode.cpp \
+ $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \
MODULE_DEFINES += \
LK_DEBUGLEVEL_NO_ALIASES=1 \
@@ -56,17 +54,21 @@
GLOBAL_INCLUDES += \
$(LOCAL_DIR)/include \
$(LOCAL_DIR)/../include \
+ $(LIBLOG_STUB_DIR)/include \
$(LIBBINDER_DIR)/include \
$(LIBBINDER_DIR)/ndk/include_cpp \
$(LIBBASE_DIR)/include \
- $(LIBCUTILS_DIR)/include \
- $(LIBUTILS_DIR)/include \
+ $(LIBUTILS_BINDER_DIR)/include \
$(FMTLIB_DIR)/include \
GLOBAL_COMPILEFLAGS += \
-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION \
-DBINDER_NO_KERNEL_IPC \
-DBINDER_RPC_SINGLE_THREADED \
+ -DBINDER_ENABLE_LIBLOG_ASSERT \
+ -DBINDER_DISABLE_NATIVE_HANDLE \
+ -DBINDER_DISABLE_BLOB \
+ -DBINDER_NO_LIBBASE \
-D__ANDROID_VNDK__ \
MODULE_DEPS += \
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 96c66a8..e0f821f 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -18,9 +18,10 @@
MODULE := $(LOCAL_DIR)
LIBBINDER_DIR := frameworks/native/libs/binder
+# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release
LIBBASE_DIR := system/libbase
-LIBCUTILS_DIR := system/core/libcutils
-LIBUTILS_DIR := system/core/libutils
+LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub
+LIBUTILS_BINDER_DIR := system/core/libutils/binder
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
@@ -43,24 +44,21 @@
$(LIBBINDER_DIR)/Status.cpp \
$(LIBBINDER_DIR)/Utils.cpp \
$(LIBBINDER_DIR)/file.cpp \
- $(LIBBASE_DIR)/hex.cpp \
- $(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/binder/Errors.cpp \
- $(LIBUTILS_DIR)/binder/RefBase.cpp \
- $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/binder/String16.cpp \
- $(LIBUTILS_DIR)/binder/String8.cpp \
- $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
- $(LIBUTILS_DIR)/binder/Unicode.cpp \
- $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
- $(LIBUTILS_DIR)/misc.cpp \
+ $(LIBUTILS_BINDER_DIR)/Errors.cpp \
+ $(LIBUTILS_BINDER_DIR)/RefBase.cpp \
+ $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \
+ $(LIBUTILS_BINDER_DIR)/String16.cpp \
+ $(LIBUTILS_BINDER_DIR)/String8.cpp \
+ $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \
+ $(LIBUTILS_BINDER_DIR)/Unicode.cpp \
+ $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \
MODULE_EXPORT_INCLUDES += \
$(LOCAL_DIR)/include \
+ $(LIBLOG_STUB_DIR)/include \
$(LIBBINDER_DIR)/include \
$(LIBBASE_DIR)/include \
- $(LIBCUTILS_DIR)/include \
- $(LIBUTILS_DIR)/include \
+ $(LIBUTILS_BINDER_DIR)/include \
$(FMTLIB_DIR)/include \
# The android/binder_to_string.h header is shared between libbinder and
@@ -70,6 +68,10 @@
MODULE_EXPORT_COMPILEFLAGS += \
-DBINDER_RPC_SINGLE_THREADED \
+ -DBINDER_ENABLE_LIBLOG_ASSERT \
+ -DBINDER_DISABLE_NATIVE_HANDLE \
+ -DBINDER_DISABLE_BLOB \
+ -DBINDER_NO_LIBBASE \
-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/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp
index d6305f8..bb573c5 100644
--- a/libs/bufferstreams/examples/app/Android.bp
+++ b/libs/bufferstreams/examples/app/Android.bp
@@ -23,6 +23,9 @@
kotlincflags: [
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
],
+ optimize: {
+ proguard_flags_files: ["proguard-rules.pro"],
+ },
resource_dirs: ["res"],
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt
index a2db934..ede7793 100644
--- a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt
@@ -10,18 +10,18 @@
* A native method that is implemented by the 'bufferstreamsdemoapp' native library, which is
* packaged with this application.
*/
- external fun stringFromJNI()
- external fun testBufferQueueCreation()
+ external fun stringFromJNI(): String;
+ external fun testBufferQueueCreation();
companion object {
- fun companion_stringFromJNI() {
+ fun companion_stringFromJNI(): String {
val instance = BufferStreamJNI()
- instance.stringFromJNI()
+ return instance.stringFromJNI()
}
fun companion_testBufferQueueCreation() {
val instance = BufferStreamJNI()
- instance.testBufferQueueCreation()
+ return instance.testBufferQueueCreation()
}
}
}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
index 46ce028..95e415e 100644
--- a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
@@ -4,10 +4,8 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
-import androidx.compose.material3.Card
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -17,17 +15,21 @@
@Composable
fun DemoScreen1(modifier: Modifier = Modifier) {
Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) {
- Card(modifier = Modifier.fillMaxWidth().weight(1f, false).padding(16.dp).height(400.dp)) {
- Text("Log output", modifier = Modifier.padding(16.dp))
- }
+ LogOutput.getInstance().LogOutputComposable()
Row(modifier = Modifier.weight(1f, false).padding(16.dp)) {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Button(
- modifier = Modifier.fillMaxWidth(),
- onClick = { BufferStreamJNI.companion_testBufferQueueCreation() }
- ) { Text("Run") }
- OutlinedButton(modifier = Modifier.fillMaxWidth(), onClick = {}) { Text("Clear") }
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { BufferStreamJNI.companion_testBufferQueueCreation() }) {
+ Text("Run")
+ }
+
+ OutlinedButton(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { LogOutput.getInstance().clearText() }) {
+ Text("Clear")
+ }
}
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt
new file mode 100644
index 0000000..3f0926f
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt
@@ -0,0 +1,65 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Card
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import java.util.Collections
+
+/*
+LogOutput centralizes logging: storing, displaying, adding, and clearing log messages with
+thread safety. It is a singleton that's also accessed from C++. The private constructor will
+not allow this class to be initialized, limiting it to getInstance().
+ */
+class LogOutput private constructor() {
+ val logs = Collections.synchronizedList(mutableStateListOf<String>())
+
+ @Composable
+ fun LogOutputComposable() {
+ val rlogs = remember { logs }
+
+ Card(modifier = Modifier.fillMaxWidth().padding(16.dp).height(400.dp)) {
+ Column(
+ modifier =
+ Modifier.padding(10.dp).size(380.dp).verticalScroll(rememberScrollState())) {
+ for (log in rlogs) {
+ Text(log, modifier = Modifier.padding(0.dp))
+ }
+ }
+ }
+ }
+
+ fun clearText() {
+ logs.clear()
+ }
+
+ fun addLog(log: String) {
+ logs.add(log)
+ }
+
+ companion object {
+ @Volatile private var instance: LogOutput? = null
+
+ @JvmStatic
+ fun getInstance(): LogOutput {
+ if (instance == null) {
+ synchronized(this) {
+ if (instance == null) {
+ instance = LogOutput()
+ }
+ }
+ }
+ return instance!!
+ }
+ }
+}
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
index f3f4404..2ccd8d7 100644
--- a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
@@ -31,17 +31,19 @@
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.android.graphics.bufferstreamsdemoapp.ui.theme.JetpackTheme
+import java.util.*
class MainActivity : ComponentActivity() {
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
setContent {
JetpackTheme {
Surface(
- modifier = Modifier.fillMaxSize(),
- color = MaterialTheme.colorScheme.background
- ) { BufferDemosApp() }
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background) {
+ BufferDemosApp()
+ }
}
}
}
@@ -67,38 +69,32 @@
val backStackEntry by navController.currentBackStackEntryAsState()
// Get the name of the current screen
val currentScreen =
- BufferDemoScreen.findByRoute(
- backStackEntry?.destination?.route ?: BufferDemoScreen.Start.route
- )
+ BufferDemoScreen.findByRoute(
+ backStackEntry?.destination?.route ?: BufferDemoScreen.Start.route)
Scaffold(
- topBar = {
- BufferDemosAppBar(
- currentScreen = currentScreen,
- canNavigateBack = navController.previousBackStackEntry != null,
- navigateUp = { navController.navigateUp() }
- )
- }
- ) {
- NavHost(
+ topBar = {
+ BufferDemosAppBar(
+ currentScreen = currentScreen,
+ canNavigateBack = navController.previousBackStackEntry != null,
+ navigateUp = { navController.navigateUp() })
+ }) {
+ NavHost(
navController = navController,
startDestination = BufferDemoScreen.Start.route,
- modifier = Modifier.padding(10.dp)
- ) {
- composable(route = BufferDemoScreen.Start.route) {
- DemoList(
- onButtonClicked = {
- navController.navigate(it)
- },
- )
- }
- composable(route = BufferDemoScreen.Demo1.route) {
- DemoScreen1(modifier = Modifier.fillMaxHeight().padding(top = 100.dp))
- }
- composable(route = BufferDemoScreen.Demo2.route) { DemoScreen2() }
- composable(route = BufferDemoScreen.Demo3.route) { DemoScreen3() }
+ modifier = Modifier.padding(10.dp)) {
+ composable(route = BufferDemoScreen.Start.route) {
+ DemoList(
+ onButtonClicked = { navController.navigate(it) },
+ )
+ }
+ composable(route = BufferDemoScreen.Demo1.route) {
+ DemoScreen1(modifier = Modifier.fillMaxHeight().padding(top = 100.dp))
+ }
+ composable(route = BufferDemoScreen.Demo2.route) { DemoScreen2() }
+ composable(route = BufferDemoScreen.Demo3.route) { DemoScreen3() }
+ }
}
- }
}
@Composable
@@ -107,25 +103,25 @@
Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) {
Column(
- modifier = Modifier.fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- Spacer(modifier = Modifier.height(100.dp))
- Text(text = "Buffer Demos", style = MaterialTheme.typography.titleLarge)
- Spacer(modifier = Modifier.height(8.dp))
- }
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ Spacer(modifier = Modifier.height(100.dp))
+ Text(text = "Buffer Demos", style = MaterialTheme.typography.titleLarge)
+ Spacer(modifier = Modifier.height(8.dp))
+ }
Row(modifier = Modifier.weight(2f, false)) {
Column(
- modifier = Modifier.fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(16.dp)
- ) {
- for (item in BufferDemoScreen.values()) {
- if (item.route != BufferDemoScreen.Start.route)
- SelectDemoButton(name = stringResource(item.title), onClick = { onButtonClicked(item.route) })
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(16.dp)) {
+ for (item in BufferDemoScreen.values()) {
+ if (item.route != BufferDemoScreen.Start.route)
+ SelectDemoButton(
+ name = stringResource(item.title),
+ onClick = { onButtonClicked(item.route) })
+ }
}
- }
}
}
}
diff --git a/libs/bufferstreams/examples/app/jni/main.cpp b/libs/bufferstreams/examples/app/jni/main.cpp
index 3d3fee4..550ad22 100644
--- a/libs/bufferstreams/examples/app/jni/main.cpp
+++ b/libs/bufferstreams/examples/app/jni/main.cpp
@@ -13,25 +13,41 @@
// limitations under the License.
#include <jni.h>
+#include <string>
#include <gui/BufferQueue.h>
-extern "C"
-{
- JNIEXPORT jstring JNICALL
- Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_stringFromJNI(
- JNIEnv* env,
- jobject /* this */) {
- const char* hello = "Hello from C++";
- return env->NewStringUTF(hello);
- }
+void log(JNIEnv* env, std::string l) {
+ jclass clazz = env->FindClass("com/android/graphics/bufferstreamsdemoapp/LogOutput");
+ jmethodID getInstance = env->GetStaticMethodID(clazz, "getInstance",
+ "()Lcom/android/graphics/bufferstreamsdemoapp/LogOutput;");
+ jmethodID addLog = env->GetMethodID(clazz, "addLog", "(Ljava/lang/String;)V");
+ jobject dmg = env->CallStaticObjectMethod(clazz, getInstance);
- JNIEXPORT void JNICALL
- Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_testBufferQueueCreation(
- JNIEnv* /* env */,
- jobject /* this */) {
- android::sp<android::IGraphicBufferProducer> producer;
- android::sp<android::IGraphicBufferConsumer> consumer;
- android::BufferQueue::createBufferQueue(&producer, &consumer);
- }
+ jstring jlog = env->NewStringUTF(l.c_str());
+ env->CallVoidMethod(dmg, addLog, jlog);
+}
+
+extern "C" {
+
+JNIEXPORT jstring JNICALL
+Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_stringFromJNI(JNIEnv* env,
+ jobject /* this */) {
+ const char* hello = "Hello from C++";
+ return env->NewStringUTF(hello);
+}
+
+JNIEXPORT void JNICALL
+Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_testBufferQueueCreation(
+ JNIEnv* env, jobject /* thiz */) {
+
+ log(env, "Calling testBufferQueueCreation.");
+ android::sp<android::IGraphicBufferProducer> producer;
+ log(env, "Created producer.");
+ android::sp<android::IGraphicBufferConsumer> consumer;
+ log(env, "Created consumer.");
+ android::BufferQueue::createBufferQueue(&producer, &consumer);
+ log(env, "Created BufferQueue successfully.");
+ log(env, "Done!");
+}
}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/proguard-rules.pro b/libs/bufferstreams/examples/app/proguard-rules.pro
new file mode 100644
index 0000000..7a987fc
--- /dev/null
+++ b/libs/bufferstreams/examples/app/proguard-rules.pro
@@ -0,0 +1,23 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+-keep,allowoptimization,allowobfuscation class com.android.graphics.bufferstreamsdemoapp.** { *; }
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/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 922b0dd..8b6f202 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);
}
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..9933680 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));
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..4d4c5e4 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -315,4 +315,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/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 412931b..c4e3ff6 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -60,9 +60,11 @@
// --- MotionPredictor ---
MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
- std::function<bool()> checkMotionPredictionEnabled)
+ std::function<bool()> checkMotionPredictionEnabled,
+ ReportAtomFunction reportAtomFunction)
: mPredictionTimestampOffsetNanos(predictionTimestampOffsetNanos),
- mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)) {}
+ mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)),
+ mReportAtomFunction(reportAtomFunction) {}
android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
@@ -90,6 +92,13 @@
mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
}
+ // Pass input event to the MetricsManager.
+ if (!mMetricsManager) {
+ mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength(),
+ mReportAtomFunction);
+ }
+ mMetricsManager->onRecord(event);
+
const int32_t action = event.getActionMasked();
if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
@@ -135,12 +144,6 @@
}
mLastEvent->copyFrom(&event, /*keepHistory=*/false);
- // Pass input event to the MetricsManager.
- if (!mMetricsManager) {
- mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength());
- }
- mMetricsManager->onRecord(event);
-
return {};
}
diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp
index 67b1032..0412d08 100644
--- a/libs/input/MotionPredictorMetricsManager.cpp
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -46,13 +46,36 @@
} // namespace
-MotionPredictorMetricsManager::MotionPredictorMetricsManager(nsecs_t predictionInterval,
- size_t maxNumPredictions)
+void MotionPredictorMetricsManager::defaultReportAtomFunction(
+ const MotionPredictorMetricsManager::AtomFields& atomFields) {
+ // Call stats_write logging function only on Android targets (not supported on host).
+#ifdef __ANDROID__
+ android::stats::libinput::
+ stats_write(android::stats::libinput::STYLUS_PREDICTION_METRICS_REPORTED,
+ /*stylus_vendor_id=*/0,
+ /*stylus_product_id=*/0,
+ atomFields.deltaTimeBucketMilliseconds,
+ atomFields.alongTrajectoryErrorMeanMillipixels,
+ atomFields.alongTrajectoryErrorStdMillipixels,
+ atomFields.offTrajectoryRmseMillipixels,
+ atomFields.pressureRmseMilliunits,
+ atomFields.highVelocityAlongTrajectoryRmse,
+ atomFields.highVelocityOffTrajectoryRmse,
+ atomFields.scaleInvariantAlongTrajectoryRmse,
+ atomFields.scaleInvariantOffTrajectoryRmse);
+#endif
+}
+
+MotionPredictorMetricsManager::MotionPredictorMetricsManager(
+ nsecs_t predictionInterval,
+ size_t maxNumPredictions,
+ ReportAtomFunction reportAtomFunction)
: mPredictionInterval(predictionInterval),
mMaxNumPredictions(maxNumPredictions),
mRecentGroundTruthPoints(maxNumPredictions + 1),
mAggregatedMetrics(maxNumPredictions),
- mAtomFields(maxNumPredictions) {}
+ mAtomFields(maxNumPredictions),
+ mReportAtomFunction(reportAtomFunction ? reportAtomFunction : defaultReportAtomFunction) {}
void MotionPredictorMetricsManager::onRecord(const MotionEvent& inputEvent) {
// Convert MotionEvent to GroundTruthPoint.
@@ -81,8 +104,8 @@
if (mRecentGroundTruthPoints.size() >= 2) {
computeAtomFields();
reportMetrics();
- break;
}
+ break;
}
}
}
@@ -345,28 +368,10 @@
}
void MotionPredictorMetricsManager::reportMetrics() {
- // Report one atom for each time bucket.
+ LOG_ALWAYS_FATAL_IF(!mReportAtomFunction);
+ // Report one atom for each prediction time bucket.
for (size_t i = 0; i < mAtomFields.size(); ++i) {
- // Call stats_write logging function only on Android targets (not supported on host).
-#ifdef __ANDROID__
- android::stats::libinput::
- stats_write(android::stats::libinput::STYLUS_PREDICTION_METRICS_REPORTED,
- /*stylus_vendor_id=*/0,
- /*stylus_product_id=*/0, mAtomFields[i].deltaTimeBucketMilliseconds,
- mAtomFields[i].alongTrajectoryErrorMeanMillipixels,
- mAtomFields[i].alongTrajectoryErrorStdMillipixels,
- mAtomFields[i].offTrajectoryRmseMillipixels,
- mAtomFields[i].pressureRmseMilliunits,
- mAtomFields[i].highVelocityAlongTrajectoryRmse,
- mAtomFields[i].highVelocityOffTrajectoryRmse,
- mAtomFields[i].scaleInvariantAlongTrajectoryRmse,
- mAtomFields[i].scaleInvariantOffTrajectoryRmse);
-#endif
- }
-
- // Set mock atom fields, if available.
- if (mMockLoggedAtomFields != nullptr) {
- *mMockLoggedAtomFields = mAtomFields;
+ mReportAtomFunction(mAtomFields[i]);
}
}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index d13a260..54eeb39 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -76,3 +76,17 @@
description: "Enable input filter rust implementation"
bug: "294546335"
}
+
+flag {
+ name: "override_key_behavior_permission_apis"
+ namespace: "input"
+ description: "enable override key behavior permission APIs"
+ bug: "309018874"
+}
+
+flag {
+ name: "remove_pointer_event_tracking_in_wm"
+ namespace: "input"
+ description: "Remove pointer event tracking in WM after the Pointer Icon Refactor"
+ bug: "315321016"
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index b60d7c9..867af79 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -272,6 +272,10 @@
return false;
};
+ if pointers.len() != pointer_properties.len() {
+ return false;
+ }
+
for pointer_property in pointer_properties.iter() {
let pointer_id = pointer_property.id;
if !pointers.contains(&pointer_id) {
@@ -592,4 +596,43 @@
)
.is_ok());
}
+
+ // Send a MOVE event with incorrect number of pointers (one of the pointers is missing).
+ #[test]
+ fn move_with_wrong_number_of_pointers() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ // POINTER 1 DOWN
+ let two_pointer_properties =
+ Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
+ | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ &two_pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
}
diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
index b420a5a..31cc145 100644
--- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp
+++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
@@ -39,6 +39,7 @@
using GroundTruthPoint = MotionPredictorMetricsManager::GroundTruthPoint;
using PredictionPoint = MotionPredictorMetricsManager::PredictionPoint;
using AtomFields = MotionPredictorMetricsManager::AtomFields;
+using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction;
inline constexpr int NANOS_PER_MILLIS = 1'000'000;
@@ -664,9 +665,16 @@
// --- MotionPredictorMetricsManager tests. ---
-// Helper function that instantiates a MetricsManager with the given mock logged AtomFields. Takes
-// vectors of ground truth and prediction points of the same length, and passes these points to the
-// MetricsManager. The format of these vectors is expected to be:
+// Creates a mock atom reporting function that appends the reported atom to the given vector.
+ReportAtomFunction createMockReportAtomFunction(std::vector<AtomFields>& reportedAtomFields) {
+ return [&reportedAtomFields](const AtomFields& atomFields) -> void {
+ reportedAtomFields.push_back(atomFields);
+ };
+}
+
+// Helper function that instantiates a MetricsManager that reports metrics to outReportedAtomFields.
+// Takes vectors of ground truth and prediction points of the same length, and passes these points
+// to the MetricsManager. The format of these vectors is expected to be:
// • groundTruthPoints: chronologically-ordered ground truth points, with at least 2 elements.
// • predictionPoints: the first index points to a vector of predictions corresponding to the
// source ground truth point with the same index.
@@ -678,15 +686,16 @@
// prediction sets (that is, excluding the first and last). Thus, groundTruthPoints and
// predictionPoints should have size at least TEST_MAX_NUM_PREDICTIONS + 2.
//
-// The passed-in outAtomFields will contain the logged AtomFields when the function returns.
+// When the function returns, outReportedAtomFields will contain the reported AtomFields.
//
// This function returns void so that it can use test assertions.
void runMetricsManager(const std::vector<GroundTruthPoint>& groundTruthPoints,
const std::vector<std::vector<PredictionPoint>>& predictionPoints,
- std::vector<AtomFields>& outAtomFields) {
+ std::vector<AtomFields>& outReportedAtomFields) {
MotionPredictorMetricsManager metricsManager(TEST_PREDICTION_INTERVAL_NANOS,
- TEST_MAX_NUM_PREDICTIONS);
- metricsManager.setMockLoggedAtomFields(&outAtomFields);
+ TEST_MAX_NUM_PREDICTIONS,
+ createMockReportAtomFunction(
+ outReportedAtomFields));
// Validate structure of groundTruthPoints and predictionPoints.
ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
@@ -712,18 +721,18 @@
// • Input: no prediction data.
// • Expectation: no metrics should be logged.
TEST(MotionPredictorMetricsManagerTest, NoPredictions) {
- std::vector<AtomFields> mockLoggedAtomFields;
+ std::vector<AtomFields> reportedAtomFields;
MotionPredictorMetricsManager metricsManager(TEST_PREDICTION_INTERVAL_NANOS,
- TEST_MAX_NUM_PREDICTIONS);
- metricsManager.setMockLoggedAtomFields(&mockLoggedAtomFields);
+ TEST_MAX_NUM_PREDICTIONS,
+ createMockReportAtomFunction(reportedAtomFields));
metricsManager.onRecord(makeMotionEvent(
GroundTruthPoint{{.position = Eigen::Vector2f(0, 0), .pressure = 0}, .timestamp = 0}));
metricsManager.onRecord(makeLiftMotionEvent());
- // Check that mockLoggedAtomFields is still empty (as it was initialized empty), ensuring that
+ // Check that reportedAtomFields is still empty (as it was initialized empty), ensuring that
// no metrics were logged.
- EXPECT_EQ(0u, mockLoggedAtomFields.size());
+ EXPECT_EQ(0u, reportedAtomFields.size());
}
// Perfect predictions test:
@@ -744,14 +753,14 @@
groundTruthPoint.timestamp += TEST_PREDICTION_INTERVAL_NANOS;
}
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
// Check that errors are all zero, or NO_DATA_SENTINEL for unreported metrics.
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
// General errors: reported for every time bucket.
@@ -764,7 +773,7 @@
EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityAlongTrajectoryRmse);
EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityOffTrajectoryRmse);
// Scale-invariant errors: reported only for the last time bucket.
- if (i + 1 == atomFields.size()) {
+ if (i + 1 == reportedAtomFields.size()) {
EXPECT_EQ(0, atom.scaleInvariantAlongTrajectoryRmse);
EXPECT_EQ(0, atom.scaleInvariantOffTrajectoryRmse);
} else {
@@ -801,14 +810,14 @@
computePressureRmses(groundTruthPoints, predictionPoints);
// Run test.
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
// Check logged metrics match expectations.
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
// Check time bucket delta matches expectation based on index and prediction interval.
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
@@ -845,14 +854,14 @@
computeGeneralPositionErrors(groundTruthPoints, predictionPoints);
// Run test.
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
// Check logged metrics match expectations.
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
// Check time bucket delta matches expectation based on index and prediction interval.
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
@@ -896,14 +905,14 @@
computeGeneralPositionErrors(groundTruthPoints, predictionPoints);
// Run test.
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
// Check logged metrics match expectations.
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
@@ -926,7 +935,7 @@
// to general errors (where reported).
//
// As above, use absolute value for RMSE, since it must be non-negative.
- if (i + 2 >= atomFields.size()) {
+ if (i + 2 >= reportedAtomFields.size()) {
EXPECT_NEAR(static_cast<int>(
1000 * std::abs(generalPositionErrors[i].alongTrajectoryErrorMean)),
atom.highVelocityAlongTrajectoryRmse, 1);
@@ -946,7 +955,7 @@
// to scale-invariant errors by dividing by `strokeVelocty * TEST_MAX_NUM_PREDICTIONS`.
//
// As above, use absolute value for RMSE, since it must be non-negative.
- if (i + 1 == atomFields.size()) {
+ if (i + 1 == reportedAtomFields.size()) {
const float pathLength = strokeVelocity * TEST_MAX_NUM_PREDICTIONS;
std::vector<float> alongTrajectoryAbsoluteErrors;
std::vector<float> offTrajectoryAbsoluteErrors;
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 4ac7ae9..3343114 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -147,4 +147,35 @@
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
+using AtomFields = MotionPredictorMetricsManager::AtomFields;
+using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction;
+
+// Creates a mock atom reporting function that appends the reported atom to the given vector.
+// The passed-in pointer must not be nullptr.
+ReportAtomFunction createMockReportAtomFunction(std::vector<AtomFields>* reportedAtomFields) {
+ return [reportedAtomFields](const AtomFields& atomFields) -> void {
+ reportedAtomFields->push_back(atomFields);
+ };
+}
+
+TEST(MotionPredictorMetricsManagerIntegrationTest, ReportsMetrics) {
+ std::vector<AtomFields> reportedAtomFields;
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+ []() { return true /*enable prediction*/; },
+ createMockReportAtomFunction(&reportedAtomFields));
+
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 1, 0ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 2, 4ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 3, 3, 8ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 4, 4, 12ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 5, 5, 16ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 6, 6, 20ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(UP, 7, 7, 24ms, /*deviceId=*/0)).ok());
+
+ // The number of atoms reported should equal the number of prediction time buckets, which is
+ // given by the prediction model's output length. For now, this value is always 5, and we
+ // hardcode it because it's not publicly accessible from the MotionPredictor.
+ EXPECT_EQ(5u, reportedAtomFields.size());
+}
+
} // namespace android
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/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/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/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index cefb140..b5cb3cb 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -125,58 +125,84 @@
void InputDeviceMetricsCollector::notifyInputDevicesChanged(
const NotifyInputDevicesChangedArgs& args) {
- reportCompletedSessions();
- onInputDevicesChanged(args.inputDeviceInfos);
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ onInputDevicesChanged(args.inputDeviceInfos);
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyConfigurationChanged(
const NotifyConfigurationChangedArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
- reportCompletedSessions();
- const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
- return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
- };
- onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
-
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
+ return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
+ };
+ onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
- reportCompletedSessions();
- onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
- [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
+ [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
const NotifyPointerCaptureChangedArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
@@ -185,10 +211,12 @@
if (isIgnoredInputDeviceId(deviceId)) {
return;
}
+ std::scoped_lock lock(mLock);
mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
}
void InputDeviceMetricsCollector::dump(std::string& dump) {
+ std::scoped_lock lock(mLock);
dump += "InputDeviceMetricsCollector:\n";
dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
@@ -196,6 +224,10 @@
dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
}
+void InputDeviceMetricsCollector::monitor() {
+ std::scoped_lock lock(mLock);
+}
+
void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 9633664..1bcd527 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -21,12 +21,14 @@
#include "NotifyArgs.h"
#include "SyncQueue.h"
+#include <android-base/thread_annotations.h>
#include <ftl/mixins.h>
#include <gui/WindowInfo.h>
#include <input/InputDevice.h>
#include <chrono>
#include <functional>
#include <map>
+#include <mutex>
#include <set>
#include <vector>
@@ -34,8 +36,6 @@
/**
* Logs metrics about registered input devices and their usages.
- *
- * All methods in the InputListenerInterface must be called from a single thread.
*/
class InputDeviceMetricsCollectorInterface : public InputListenerInterface {
public:
@@ -50,6 +50,9 @@
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
+
+ /** Called by the heartbeat to ensure that this component has not deadlocked. */
+ virtual void monitor() = 0;
};
/** The logging interface for the metrics collector, injected for testing. */
@@ -116,10 +119,12 @@
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) override;
void dump(std::string& dump) override;
+ void monitor() override;
private:
+ std::mutex mLock;
InputListenerInterface& mNextListener;
- InputDeviceMetricsLogger& mLogger;
+ InputDeviceMetricsLogger& mLogger GUARDED_BY(mLock);
const std::chrono::nanoseconds mUsageSessionTimeout;
// Type-safe wrapper for input device id.
@@ -135,10 +140,10 @@
using Uid = gui::Uid;
using MetricsDeviceInfo = InputDeviceMetricsLogger::MetricsDeviceInfo;
- std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos;
+ std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos GUARDED_BY(mLock);
using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>;
- SyncQueue<Interaction> mInteractionsQueue;
+ SyncQueue<Interaction> mInteractionsQueue GUARDED_BY(mLock);
class ActiveSession {
public:
@@ -166,16 +171,16 @@
};
// The input devices that currently have active usage sessions.
- std::map<DeviceId, ActiveSession> mActiveUsageSessions;
+ std::map<DeviceId, ActiveSession> mActiveUsageSessions GUARDED_BY(mLock);
- void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos);
- void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info);
+ void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) REQUIRES(mLock);
+ void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info) REQUIRES(mLock);
using SourceProvider =
std::function<std::set<InputDeviceUsageSource>(const MetricsDeviceInfo&)>;
void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
- const SourceProvider& getSources);
- void onInputDeviceInteraction(const Interaction&);
- void reportCompletedSessions();
+ const SourceProvider& getSources) REQUIRES(mLock);
+ void onInputDeviceInteraction(const Interaction&) REQUIRES(mLock);
+ void reportCompletedSessions() REQUIRES(mLock);
};
} // namespace android
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 1b8fad3..5d87d34 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -22,16 +22,19 @@
using aidl::com::android::server::inputflinger::IInputFilter;
using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+using aidl::com::android::server::inputflinger::KeyEventAction;
+using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo;
+using aidl::android::hardware::input::common::Source;
AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) {
AidlKeyEvent event;
event.id = args.id;
event.eventTime = args.eventTime;
event.deviceId = args.deviceId;
- event.source = args.source;
+ event.source = static_cast<Source>(args.source);
event.displayId = args.displayId;
event.policyFlags = args.policyFlags;
- event.action = args.action;
+ event.action = static_cast<KeyEventAction>(args.action);
event.flags = args.flags;
event.keyCode = args.keyCode;
event.scanCode = args.scanCode;
@@ -41,41 +44,23 @@
return event;
}
-NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
- return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, event.source,
- event.displayId, event.policyFlags, 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);
}
void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
if (isFilterEnabled()) {
- std::vector<int32_t> deviceIds;
+ std::vector<AidlDeviceInfo> deviceInfos;
for (auto info : args.inputDeviceInfos) {
- deviceIds.push_back(info.getId());
+ AidlDeviceInfo aidlInfo;
+ aidlInfo.deviceId = info.getId();
+ aidlInfo.external = info.isExternal();
+ deviceInfos.push_back(aidlInfo);
}
- LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceIds).isOk());
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceInfos).isOk());
}
mNextListener.notify(args);
}
@@ -85,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) {
@@ -122,6 +107,24 @@
return result;
}
+void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) {
+ std::scoped_lock _l(mLock);
+
+ if (mConfig.bounceKeysThresholdNs != threshold) {
+ mConfig.bounceKeysThresholdNs = threshold;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+ }
+}
+
+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 699f3a0..9fa7a87 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -17,6 +17,8 @@
#pragma once
#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <utils/Mutex.h>
+#include "InputFilterCallbacks.h"
#include "InputListener.h"
#include "NotifyArgs.h"
@@ -31,6 +33,8 @@
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
+ virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0;
+ virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0;
};
class InputFilter : public InputFilterInterface {
@@ -39,6 +43,8 @@
using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
using IInputFilterCallbacks =
aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks;
+ using InputFilterConfiguration =
+ aidl::com::android::server::inputflinger::InputFilterConfiguration;
explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&);
~InputFilter() override = default;
@@ -51,12 +57,16 @@
void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
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);
bool isFilterEnabled();
};
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/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 8cf61f9..296f244 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -224,10 +224,17 @@
return *mDispatcher;
}
+InputFilterInterface& InputManager::getInputFilter() {
+ return *mInputFilter;
+}
+
void InputManager::monitor() {
mReader->monitor();
mBlocker->monitor();
mProcessor->monitor();
+ if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
+ mCollector->monitor();
+ }
mDispatcher->monitor();
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index aea7bd5..fa7db37 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -102,6 +102,9 @@
/* Gets the input dispatcher. */
virtual InputDispatcherInterface& getDispatcher() = 0;
+ /* Gets the input filter */
+ virtual InputFilterInterface& getInputFilter() = 0;
+
/* Check that the input stages have not deadlocked. */
virtual void monitor() = 0;
@@ -126,6 +129,7 @@
InputProcessorInterface& getProcessor() override;
InputDeviceMetricsCollectorInterface& getMetricsCollector() override;
InputDispatcherInterface& getDispatcher() override;
+ InputFilterInterface& getInputFilter() override;
void monitor() override;
void dump(std::string& dump) override;
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/Android.bp b/services/inputflinger/aidl/Android.bp
index 314c433..d068129 100644
--- a/services/inputflinger/aidl/Android.bp
+++ b/services/inputflinger/aidl/Android.bp
@@ -17,6 +17,9 @@
srcs: ["**/*.aidl"],
unstable: true,
host_supported: true,
+ imports: [
+ "android.hardware.input.common-V1",
+ ],
backend: {
cpp: {
enabled: false,
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
new file mode 100644
index 0000000..b9e6a03
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package com.android.server.inputflinger;
+
+/**
+ * Analogous to Android's InputDeviceInfo
+ * Stores the basic information connected input devices.
+ */
+parcelable DeviceInfo {
+ int deviceId;
+ boolean external;
+}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
index 44f959e..2921d30 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -16,6 +16,8 @@
package com.android.server.inputflinger;
+import com.android.server.inputflinger.DeviceInfo;
+import com.android.server.inputflinger.InputFilterConfiguration;
import com.android.server.inputflinger.KeyEvent;
/**
@@ -31,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 */
@@ -40,6 +45,9 @@
void notifyKey(in KeyEvent event);
/** Notifies if any InputDevice list changed and provides the list of connected peripherals */
- void notifyInputDevicesChanged(in int[] deviceIds);
+ void notifyInputDevicesChanged(in DeviceInfo[] deviceInfos);
+
+ /** Notifies when configuration changes */
+ void notifyConfigurationChanged(in InputFilterConfiguration config);
}
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
new file mode 100644
index 0000000..38b1612
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package com.android.server.inputflinger;
+
+/**
+ * Contains data for the current Input filter configuration
+ */
+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/aidl/com/android/server/inputflinger/KeyEvent.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
index e213221..2cae6e1 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
@@ -16,20 +16,24 @@
package com.android.server.inputflinger;
+import android.hardware.input.common.Source;
+import com.android.server.inputflinger.KeyEventAction;
+
/**
* Analogous to Android's native KeyEvent / NotifyKeyArgs.
* Stores the basic information about Key events.
*/
+@RustDerive(Copy=true, Clone=true, Eq=true, PartialEq=true)
parcelable KeyEvent {
int id;
int deviceId;
long downTime;
long readTime;
long eventTime;
- int source;
+ Source source;
int displayId;
int policyFlags;
- int action;
+ KeyEventAction action;
int flags;
int keyCode;
int scanCode;
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl
new file mode 100644
index 0000000..43ee5fe
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.server.inputflinger;
+
+/** Different Key event actions */
+enum KeyEventAction {
+ /** The key has been pressed down. */
+ DOWN = 0,
+
+ /** The key has been released. */
+ UP = 1,
+
+ /**
+ * Multiple duplicate key events have occurred in a row, or a
+ * complex string is being delivered. The repeat_count property
+ * of the key event contains the number of times the given key
+ * code should be executed.
+ *
+ * NOTE: This is deprecated and should never be used. This just
+ * for consistency with KeyEvent actions defined in NotifyKeyArgs.
+ */
+ MULTIPLE = 2
+}
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 1a94f41..6033398 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -126,10 +126,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>";
@@ -5129,7 +5125,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
@@ -5687,33 +5683,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 +5773,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 {
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 2dd05f5..5a74a42 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -35,12 +35,10 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
- // The evdev multi-touch protocol does not allow userspace applications to query the initial or
- // current state of the pointers at any time. This means if we clear our accumulated state when
- // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
- // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
- // rebuilding the state from scratch, we work around this kernel API limitation by never
- // fully clearing any state specific to the multi-touch protocol.
+ // TODO(b/291626046): Sync the MT state with the kernel using EVIOCGMTSLOTS.
+ mMultiTouchMotionAccumulator.reset(getDeviceContext());
+ mPointerIdBits.clear();
+
return TouchInputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b0fc903..d06514a 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -30,23 +30,29 @@
size_t slotCount, bool usingSlotsProtocol) {
mUsingSlotsProtocol = usingSlotsProtocol;
mSlots = std::vector<Slot>(slotCount);
+ reset(deviceContext);
+}
- mCurrentSlot = -1;
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot before we
- // start reading events from the device. It is possible that the current slot index will
- // not be the same as it was when the first event was written into the evdev buffer, which
- // means the input mapper could start out of sync with the initial state of the events in
- // the evdev buffer. In the extremely unlikely case that this happens, the data from two
- // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
- // touch point to "jump", but at least there will be no stuck touches.
- int32_t initialSlot;
- if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- status == OK) {
- mCurrentSlot = initialSlot;
- } else {
- ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
- }
+void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) {
+ resetSlots();
+
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+
+ // Query the driver for the current slot index and use it as the initial slot before we
+ // start reading events from the device. It is possible that the current slot index will
+ // not be the same as it was when the first event was written into the evdev buffer, which
+ // means the input mapper could start out of sync with the initial state of the events in
+ // the evdev buffer. In the extremely unlikely case that this happens, the data from two
+ // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
+ // touch point to "jump", but at least there will be no stuck touches.
+ int32_t initialSlot;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 0e3e2bb..5b55e3d 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -83,6 +83,7 @@
LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index);
return mSlots[index];
}
+ void reset(const InputDeviceContext& deviceContext);
private:
int32_t mCurrentSlot;
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 2775bcc..2803805 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -38,6 +38,7 @@
rustlibs: [
"libcxx",
"com.android.server.inputflinger-rust",
+ "android.hardware.input.common-V1-rust",
"libbinder_rs",
"liblog_rust",
"liblogger",
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
new file mode 100644
index 0000000..894b881
--- /dev/null
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+//! Bounce keys input filter implementation.
+//! Bounce keys is an accessibility feature to aid users who have physical disabilities, that
+//! allows the user to configure the device to ignore rapid, repeated key presses of the same key.
+use crate::input_filter::Filter;
+
+use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+};
+use log::debug;
+use std::collections::{HashMap, HashSet};
+
+#[derive(Debug)]
+struct LastUpKeyEvent {
+ keycode: i32,
+ event_time: i64,
+}
+
+#[derive(Debug)]
+struct BlockedEvent {
+ device_id: i32,
+ keycode: i32,
+}
+
+pub struct BounceKeysFilter {
+ next: Box<dyn Filter + Send + Sync>,
+ key_event_map: HashMap<i32, LastUpKeyEvent>,
+ blocked_events: Vec<BlockedEvent>,
+ external_devices: HashSet<i32>,
+ bounce_key_threshold_ns: i64,
+}
+
+impl BounceKeysFilter {
+ /// Create a new BounceKeysFilter instance.
+ pub fn new(
+ next: Box<dyn Filter + Send + Sync>,
+ bounce_key_threshold_ns: i64,
+ ) -> BounceKeysFilter {
+ Self {
+ next,
+ key_event_map: HashMap::new(),
+ blocked_events: Vec::new(),
+ external_devices: HashSet::new(),
+ bounce_key_threshold_ns,
+ }
+ }
+}
+
+impl Filter for BounceKeysFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ if !(self.external_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) {
+ self.next.notify_key(event);
+ return;
+ }
+ match event.action {
+ KeyEventAction::DOWN => match self.key_event_map.get(&event.deviceId) {
+ None => self.next.notify_key(event),
+ Some(last_up_event) => {
+ if event.keyCode == last_up_event.keycode
+ && event.eventTime < last_up_event.event_time + self.bounce_key_threshold_ns
+ {
+ self.blocked_events.push(BlockedEvent {
+ device_id: event.deviceId,
+ keycode: event.keyCode,
+ });
+ debug!("Event dropped because last up was too recent");
+ } else {
+ self.key_event_map.remove(&event.deviceId);
+ self.next.notify_key(event);
+ }
+ }
+ },
+ KeyEventAction::UP => {
+ self.key_event_map.insert(
+ event.deviceId,
+ LastUpKeyEvent { keycode: event.keyCode, event_time: event.eventTime },
+ );
+ if let Some(index) = self.blocked_events.iter().position(|blocked_event| {
+ blocked_event.device_id == event.deviceId
+ && blocked_event.keycode == event.keyCode
+ }) {
+ self.blocked_events.remove(index);
+ debug!("Event dropped because key down was already dropped");
+ } else {
+ self.next.notify_key(event);
+ }
+ }
+ _ => (),
+ }
+ }
+
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
+ self.key_event_map.retain(|id, _| device_infos.iter().any(|x| *id == x.deviceId));
+ self.blocked_events.retain(|blocked_event| {
+ device_infos.iter().any(|x| blocked_event.device_id == x.deviceId)
+ });
+ self.external_devices.clear();
+ for device_info in device_infos {
+ if device_info.external {
+ self.external_devices.insert(device_info.deviceId);
+ }
+ }
+ self.next.notify_devices_changed(device_infos);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::bounce_keys_filter::BounceKeysFilter;
+ use crate::input_filter::{test_filter::TestFilter, Filter};
+ use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+ };
+
+ static BASE_KEY_EVENT: KeyEvent = KeyEvent {
+ id: 1,
+ deviceId: 1,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: Source::KEYBOARD,
+ displayId: 0,
+ policyFlags: 0,
+ action: KeyEventAction::DOWN,
+ flags: 0,
+ keyCode: 1,
+ scanCode: 0,
+ metaState: 0,
+ };
+
+ #[test]
+ fn test_is_notify_key_for_external_keyboard() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_doesnt_block_for_internal_keyboard() {
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_internal_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_doesnt_block_for_external_stylus() {
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+
+ let event =
+ KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event =
+ KeyEvent { action: KeyEventAction::UP, source: Source::STYLUS, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event =
+ KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_for_multiple_external_keyboards() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_devices(
+ Box::new(next.clone()),
+ &[
+ DeviceInfo { deviceId: 1, external: true },
+ DeviceInfo { deviceId: 2, external: true },
+ ],
+ 100, /* threshold */
+ );
+
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { deviceId: 2, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ fn setup_filter_with_external_device(
+ next: Box<dyn Filter + Send + Sync>,
+ device_id: i32,
+ threshold: i64,
+ ) -> BounceKeysFilter {
+ setup_filter_with_devices(
+ next,
+ &[DeviceInfo { deviceId: device_id, external: true }],
+ threshold,
+ )
+ }
+
+ fn setup_filter_with_internal_device(
+ next: Box<dyn Filter + Send + Sync>,
+ device_id: i32,
+ threshold: i64,
+ ) -> BounceKeysFilter {
+ setup_filter_with_devices(
+ next,
+ &[DeviceInfo { deviceId: device_id, external: false }],
+ threshold,
+ )
+ }
+
+ fn setup_filter_with_devices(
+ next: Box<dyn Filter + Send + Sync>,
+ devices: &[DeviceInfo],
+ threshold: i64,
+ ) -> BounceKeysFilter {
+ let mut filter = BounceKeysFilter::new(next, threshold);
+ filter.notify_devices_changed(devices);
+ filter
+ }
+}
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index 5851877..e94a71f 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -16,17 +16,40 @@
//! InputFilter manages all the filtering components that can intercept events, modify the events,
//! block events, etc depending on the situation. This will be used support Accessibility features
-//! like Slow keys, Bounce keys, etc.
+//! like Sticky keys, Slow keys, Bounce keys, etc.
use binder::{Interface, Strong};
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo,
IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ InputFilterConfiguration::InputFilterConfiguration,
KeyEvent::KeyEvent,
};
+use crate::bounce_keys_filter::BounceKeysFilter;
+use crate::sticky_keys_filter::StickyKeysFilter;
+use log::{error, info};
+use std::sync::{Arc, Mutex, RwLock};
+
+/// Interface for all the sub input filters
+pub trait Filter {
+ fn notify_key(&mut self, event: &KeyEvent);
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]);
+}
+
+struct InputFilterState {
+ first_filter: Box<dyn Filter + Send + Sync>,
+ enabled: bool,
+}
+
/// The rust implementation of InputFilter
pub struct InputFilter {
- callbacks: Strong<dyn IInputFilterCallbacks>,
+ // In order to have multiple immutable references to the callbacks that is thread safe need to
+ // wrap the callbacks in Arc<RwLock<...>>
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ // Access to mutable references to mutable state (includes access to filters, enabled, etc.) is
+ // guarded by Mutex for thread safety
+ state: Mutex<InputFilterState>,
}
impl Interface for InputFilter {}
@@ -34,72 +57,187 @@
impl InputFilter {
/// Create a new InputFilter instance.
pub fn new(callbacks: Strong<dyn IInputFilterCallbacks>) -> InputFilter {
- Self { callbacks }
+ let ref_callbacks = Arc::new(RwLock::new(callbacks));
+ let base_filter = Box::new(BaseFilter::new(ref_callbacks.clone()));
+ Self::create_input_filter(base_filter, ref_callbacks)
+ }
+
+ /// Create test instance of InputFilter
+ fn create_input_filter(
+ first_filter: Box<dyn Filter + Send + Sync>,
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ ) -> InputFilter {
+ Self { callbacks, state: Mutex::new(InputFilterState { first_filter, enabled: false }) }
}
}
impl IInputFilter for InputFilter {
fn isEnabled(&self) -> binder::Result<bool> {
- // TODO(b/294546335): Return true if any filters are to be applied, false otherwise
- Result::Ok(false)
+ Result::Ok(self.state.lock().unwrap().enabled)
}
+
fn notifyKey(&self, event: &KeyEvent) -> binder::Result<()> {
- // TODO(b/294546335): Handle key event and modify key events here
- // Just send back the event without processing for now.
- let _ = self.callbacks.sendKeyEvent(event);
+ let first_filter = &mut self.state.lock().unwrap().first_filter;
+ first_filter.notify_key(event);
Result::Ok(())
}
- fn notifyInputDevicesChanged(&self, _device_ids: &[i32]) -> binder::Result<()> {
- // TODO(b/294546335): Update data based on device changes here
+
+ fn notifyInputDevicesChanged(&self, device_infos: &[DeviceInfo]) -> binder::Result<()> {
+ let first_filter = &mut self.state.lock().unwrap().first_filter;
+ first_filter.notify_devices_changed(device_infos);
Result::Ok(())
}
+
+ fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> {
+ 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));
+ state.enabled = true;
+ info!("Bounce keys filter is installed");
+ }
+ state.first_filter = first_filter;
+ Result::Ok(())
+ }
+}
+
+struct BaseFilter {
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+}
+
+impl BaseFilter {
+ fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> BaseFilter {
+ Self { callbacks }
+ }
+}
+
+impl Filter for BaseFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ match self.callbacks.read().unwrap().sendKeyEvent(event) {
+ Ok(_) => (),
+ _ => error!("Failed to send key event back to native C++"),
+ }
+ }
+
+ fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
+ // do nothing
+ }
+}
+
+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::InputFilter;
- use binder::{Interface, Strong};
- use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
- IInputFilter::IInputFilter, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
- KeyEvent::KeyEvent,
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, InputFilter,
};
-
- struct FakeCallbacks {}
-
- impl Interface for FakeCallbacks {}
-
- impl IInputFilterCallbacks for FakeCallbacks {
- fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> {
- Result::Ok(())
- }
- }
+ 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::IInputFilter,
+ InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent,
+ KeyEventAction::KeyEventAction,
+ };
+ use std::sync::{Arc, RwLock};
#[test]
- fn test_is_enabled() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
- let result = filter.isEnabled();
+ fn test_not_enabled_with_default_filter() {
+ 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());
}
#[test]
- fn test_notify_key() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+ fn test_notify_key_with_no_filters() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks.clone())));
let event = create_key_event();
- assert!(filter.notifyKey(&event).is_ok());
+ 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 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);
}
#[test]
fn test_notify_devices_changed() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
- let result = filter.notifyInputDevicesChanged(&[0]);
+ let test_filter = TestFilter::new();
+ 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());
+ assert!(test_filter.is_device_changed_called());
+ }
+
+ #[test]
+ fn test_notify_configuration_changed_enabled_bounce_keys() {
+ 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());
+ }
+
+ #[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 {
@@ -109,10 +247,10 @@
downTime: 0,
readTime: 0,
eventTime: 0,
- source: 0,
+ source: Source::KEYBOARD,
displayId: 0,
policyFlags: 0,
- action: 0,
+ action: KeyEventAction::DOWN,
flags: 0,
keyCode: 0,
scanCode: 0,
@@ -120,3 +258,118 @@
}
}
}
+
+#[cfg(test)]
+pub mod test_filter {
+ use crate::input_filter::Filter;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent,
+ };
+ use std::sync::{Arc, RwLock, RwLockWriteGuard};
+
+ #[derive(Default)]
+ struct TestFilterInner {
+ is_device_changed_called: bool,
+ last_event: Option<KeyEvent>,
+ }
+
+ #[derive(Default, Clone)]
+ pub struct TestFilter(Arc<RwLock<TestFilterInner>>);
+
+ impl TestFilter {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ fn inner(&mut self) -> RwLockWriteGuard<'_, TestFilterInner> {
+ 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
+ }
+
+ pub fn is_device_changed_called(&self) -> bool {
+ self.0.read().unwrap().is_device_changed_called
+ }
+ }
+
+ impl Filter for TestFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ self.inner().last_event = Some(*event);
+ }
+ fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
+ self.inner().is_device_changed_called = true;
+ }
+ }
+}
+
+#[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 a4049d5..fa16898 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -19,7 +19,9 @@
//! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and
//! pass it back to C++ as a local AIDL interface.
+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/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index b55c9cc..6d6b7d8 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -17,6 +17,7 @@
#include "CursorInputMapper.h"
#include <android-base/logging.h>
+#include <com_android_input_flags.h>
#include <gtest/gtest.h>
#include "FakePointerController.h"
@@ -38,6 +39,12 @@
constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+constexpr int32_t DISPLAY_ID = 0;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+
+namespace input_flags = com::android::input::flags;
/**
* Unit tests for CursorInputMapper.
@@ -60,6 +67,11 @@
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
.WillRepeatedly(Return(false));
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT,
+ ViewportType::INTERNAL);
+
mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
}
@@ -139,6 +151,7 @@
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(ACTION_MOVE),
WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), WithCoords(10.0f, 20.0f),
+ WithRelativeMotion(10.0f, 20.0f),
WithCursorPosition(INVALID_CURSOR_POSITION,
INVALID_CURSOR_POSITION)))));
@@ -178,12 +191,17 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(ACTION_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
- WithCoords(30.0f, 40.0f)))));
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), WithCoords(30.0f, 40.0f),
+ WithRelativeMotion(30.0f, 40.0f)))));
// Disable pointer capture. Afterwards, events should be generated the usual way.
setPointerCapture(false);
-
+ const auto expectedCoords = input_flags::enable_pointer_choreographer()
+ ? WithCoords(0, 0)
+ : WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
+ const auto expectedCursorPosition = input_flags::enable_pointer_choreographer()
+ ? WithCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION)
+ : WithCursorPosition(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
args.clear();
args += process(EV_REL, REL_X, 10);
args += process(EV_REL, REL_Y, 20);
@@ -191,9 +209,8 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
- WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f),
- WithCursorPosition(INITIAL_CURSOR_X + 10.0f,
- INITIAL_CURSOR_Y + 20.0f)))));
+ expectedCoords, expectedCursorPosition,
+ WithRelativeMotion(10.0f, 20.0f)))));
}
} // namespace android
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/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index e8b779a..e0a3e94 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -232,6 +232,11 @@
mResetWasCalled = false;
}
+ void assertResetWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mResetWasCalled) << "Expected reset to not have been called.";
+ }
+
void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -248,6 +253,11 @@
mProcessWasCalled = false;
}
+ void assertProcessWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mProcessWasCalled) << "Expected process to not have been called.";
+ }
+
void setKeyCodeState(int32_t keyCode, int32_t state) {
mKeyCodeStates.replaceValueFor(keyCode, state);
}
@@ -2872,6 +2882,60 @@
ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
}
+TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) {
+ mFakePolicy->clearViewports();
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+
+ mapper.assertConfigureWasCalled();
+ mapper.assertResetWasNotCalled();
+
+ RawEvent event{.deviceId = EVENTHUB_ID,
+ .readTime = ARBITRARY_TIME,
+ .when = ARBITRARY_TIME,
+ .type = EV_SYN,
+ .code = SYN_REPORT,
+ .value = 0};
+
+ // Events are processed normally.
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasCalled();
+
+ // Simulate a kernel buffer overflow, which generates a SYN_DROPPED event.
+ // This should reset the mapper.
+ event.type = EV_SYN;
+ event.code = SYN_DROPPED;
+ event.value = 0;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+ mapper.assertResetWasCalled();
+
+ // All events until the next SYN_REPORT should be dropped.
+ event.type = EV_KEY;
+ event.code = KEY_A;
+ event.value = 1;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+
+ // We get the SYN_REPORT event now, which is not forwarded to mappers.
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ event.value = 0;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+
+ // The mapper receives events normally now.
+ event.type = EV_KEY;
+ event.code = KEY_B;
+ event.value = 1;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasCalled();
+}
+
// --- SwitchInputMapperTest ---
class SwitchInputMapperTest : public InputMapperTest {
@@ -10907,7 +10971,7 @@
ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
}
-TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+TEST_F(MultiTouchInputMapperTest, ResetClearsTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
@@ -10930,25 +10994,36 @@
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN)));
- // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
- // preserved. Resetting should cancel the ongoing gesture.
+ // Reset the mapper. When the mapper is reset, the touch state is also cleared.
resetMapper(mapper, ARBITRARY_TIME);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
- // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
- // the existing touch state to generate a down event.
+ // Move the second slot pointer, and ensure there are no events, because the touch state was
+ // cleared and no slots should be in use.
processPosition(mapper, 301, 302);
processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Release both fingers.
+ processId(mapper, INVALID_TRACKING_ID);
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Start a new gesture, and ensure we get a DOWN event for it.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 200, 300);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f))));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(ACTION_POINTER_1_DOWN), WithPressure(1.f))));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
-TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
+TEST_F(MultiTouchInputMapperTest, ResetClearsTouchStateWithNoPointersDown) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
@@ -11076,6 +11151,66 @@
ASSERT_FALSE(fakePointerController->isPointerShown());
}
+TEST_F(MultiTouchInputMapperTest, SimulateKernelBufferOverflow) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
+
+ // First finger down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Assume the kernel buffer overflows, and we get a SYN_DROPPED event.
+ // This will reset the mapper, and thus also reset the touch state.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_DROPPED, 0);
+ resetMapper(mapper, ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
+
+ // Since the touch state was reset, it doesn't know which slots are active, so any movements
+ // are ignored.
+ processPosition(mapper, 101, 201);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Second finger goes down. This is the first active finger, so we get a DOWN event.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, 400, 500);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // First slot is still ignored, only the second one is active.
+ processSlot(mapper, FIRST_SLOT);
+ processPosition(mapper, 102, 202);
+ processSlot(mapper, SECOND_SLOT);
+ processPosition(mapper, 401, 501);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Both slots up, so we get the UP event for the active pointer.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
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/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
index 214649c..dc5a213 100644
--- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -41,14 +41,16 @@
class NotifyStreamProvider {
public:
NotifyStreamProvider(FuzzedDataProvider& fdp)
- : mFdp(fdp), mIdGenerator(IdGenerator::Source::OTHER), mVerifier("Fuzz verifier") {}
+ : mFdp(fdp), mIdGenerator(IdGenerator::Source::OTHER) {}
std::optional<NotifyMotionArgs> nextMotion() {
NotifyMotionArgs args = generateFuzzedMotionArgs(mIdGenerator, mFdp, MAX_RANDOM_DISPLAYS);
+ auto [it, _] = mVerifiers.emplace(args.displayId, "Fuzz Verifier");
+ InputVerifier& verifier = it->second;
const Result<void> result =
- mVerifier.processMovement(args.deviceId, args.source, args.action,
- args.getPointerCount(), args.pointerProperties.data(),
- args.pointerCoords.data(), args.flags);
+ verifier.processMovement(args.deviceId, args.source, args.action,
+ args.getPointerCount(), args.pointerProperties.data(),
+ args.pointerCoords.data(), args.flags);
if (result.ok()) {
return args;
}
@@ -60,9 +62,30 @@
IdGenerator mIdGenerator;
- InputVerifier mVerifier;
+ 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,
@@ -71,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;
}
@@ -111,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/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..6b74f6f
--- /dev/null
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -0,0 +1,8 @@
+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"
+}
\ No newline at end of file
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/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index f74ef4c..7253354 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -38,11 +38,10 @@
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
- MOCK_METHOD(bool, ensurePowerHintSessionRunning, (), (override));
MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
MOCK_METHOD(void, reportActualWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
- MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
MOCK_METHOD(void, setGpuFenceTime,
(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
MOCK_METHOD(void, setHwcValidateTiming,
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 e005ad3..dd228b4 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -50,7 +50,6 @@
namespace impl {
using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::Mode;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::WorkDuration;
@@ -144,11 +143,13 @@
if (!mBootFinished.load()) {
return;
}
- if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ if (usePowerHintSession()) {
std::lock_guard lock(mHintSessionMutex);
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
- if (!ret.isOk()) {
- mHintSessionRunning = false;
+ if (ensurePowerHintSessionRunning()) {
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
+ if (!ret.isOk()) {
+ mHintSession = nullptr;
+ }
}
}
}
@@ -162,11 +163,13 @@
if (mSendUpdateImminent.exchange(false)) {
ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
- if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ if (usePowerHintSession()) {
std::lock_guard lock(mHintSessionMutex);
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
- if (!ret.isOk()) {
- mHintSessionRunning = false;
+ if (ensurePowerHintSessionRunning()) {
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
+ if (!ret.isOk()) {
+ mHintSession = nullptr;
+ }
}
}
@@ -193,14 +196,12 @@
}
}
-// checks both if it supports and if it's enabled
bool PowerAdvisor::usePowerHintSession() {
// uses cached value since the underlying support and flag are unlikely to change at runtime
return mHintSessionEnabled.value_or(false) && supportsPowerHintSession();
}
bool PowerAdvisor::supportsPowerHintSession() {
- // cache to avoid needing lock every time
if (!mSupportsHintSession.has_value()) {
mSupportsHintSession = getPowerHal().getHintSessionPreferredRate().isOk();
}
@@ -208,10 +209,15 @@
}
bool PowerAdvisor::ensurePowerHintSessionRunning() {
- if (!mHintSessionRunning && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
- startPowerHintSession(mHintSessionThreadIds);
+ if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
+ auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
+ mHintSessionThreadIds, mTargetDuration.ns());
+
+ if (ret.isOk()) {
+ mHintSession = ret.value();
+ }
}
- return mHintSessionRunning;
+ return mHintSession != nullptr;
}
void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) {
@@ -223,15 +229,16 @@
{
mTargetDuration = targetDuration;
if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
- if (ensurePowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
+ if (targetDuration == mLastTargetDurationSent) return;
+ std::lock_guard lock(mHintSessionMutex);
+ if (ensurePowerHintSessionRunning()) {
ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
mLastTargetDurationSent = targetDuration;
- std::lock_guard lock(mHintSessionMutex);
auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
ret.getDescription().c_str());
- mHintSessionRunning = false;
+ mHintSession = nullptr;
}
}
}
@@ -244,21 +251,12 @@
}
ATRACE_CALL();
std::optional<Duration> actualDuration = estimateWorkDuration();
- if (!actualDuration.has_value() || actualDuration < 0ns || !ensurePowerHintSessionRunning()) {
+ if (!actualDuration.has_value() || actualDuration < 0ns) {
ALOGV("Failed to send actual work duration, skipping");
return;
}
actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin);
mActualDuration = actualDuration;
- WorkDuration duration;
- duration.workPeriodStartTimestampNanos = mCommitStartTimes[0].ns();
- // TODO(b/284324521): Correctly calculate total duration.
- duration.durationNanos = actualDuration->ns();
- duration.cpuDurationNanos = actualDuration->ns();
- // TODO(b/284324521): Calculate RenderEngine GPU time.
- duration.gpuDurationNanos = 0;
- duration.timeStampNanos = TimePoint::now().ns();
- mHintSessionQueue.push_back(duration);
if (sTraceHintSessionData) {
ATRACE_INT64("Measured duration", actualDuration->ns());
@@ -274,13 +272,34 @@
actualDuration->ns(), mLastTargetDurationSent.ns(),
Duration{*actualDuration - mLastTargetDurationSent}.ns());
+ if (mTimingTestingMode) {
+ mDelayReportActualMutexAcquisitonPromise.get_future().wait();
+ mDelayReportActualMutexAcquisitonPromise = std::promise<bool>{};
+ }
+
{
std::lock_guard lock(mHintSessionMutex);
+ if (!ensurePowerHintSessionRunning()) {
+ ALOGV("Hint session not running and could not be started, skipping");
+ return;
+ }
+
+ WorkDuration duration{
+ .timeStampNanos = TimePoint::now().ns(),
+ // TODO(b/284324521): Correctly calculate total duration.
+ .durationNanos = actualDuration->ns(),
+ .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
+ .cpuDurationNanos = actualDuration->ns(),
+ // TODO(b/284324521): Calculate RenderEngine GPU time.
+ .gpuDurationNanos = 0,
+ };
+ mHintSessionQueue.push_back(duration);
+
auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
if (!ret.isOk()) {
ALOGW("Failed to report actual work durations with error: %s",
ret.getDescription().c_str());
- mHintSessionRunning = false;
+ mHintSession = nullptr;
return;
}
}
@@ -291,7 +310,8 @@
mHintSessionEnabled = enabled;
}
-bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) {
+bool PowerAdvisor::startPowerHintSession(std::vector<int32_t>&& threadIds) {
+ mHintSessionThreadIds = threadIds;
if (!mBootFinished.load()) {
return false;
}
@@ -299,25 +319,14 @@
ALOGI("Cannot start power hint session: disabled or unsupported");
return false;
}
- if (mHintSessionRunning) {
+ LOG_ALWAYS_FATAL_IF(mHintSessionThreadIds.empty(),
+ "No thread IDs provided to power hint session!");
+ std::lock_guard lock(mHintSessionMutex);
+ if (mHintSession != nullptr) {
ALOGE("Cannot start power hint session: already running");
return false;
}
- LOG_ALWAYS_FATAL_IF(threadIds.empty(), "No thread IDs provided to power hint session!");
- {
- std::lock_guard lock(mHintSessionMutex);
- mHintSession = nullptr;
- mHintSessionThreadIds = threadIds;
-
- auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
- threadIds, mTargetDuration.ns());
-
- if (ret.isOk()) {
- mHintSessionRunning = true;
- mHintSession = ret.value();
- }
- }
- return mHintSessionRunning;
+ return ensurePowerHintSessionRunning();
}
void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 05e4c8b..0276e44 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -46,16 +46,15 @@
// Initializes resources that cannot be initialized on construction
virtual void init() = 0;
+ // Used to indicate that power hints can now be reported
virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
- virtual void notifyCpuLoadUp() = 0;
- virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
- // Checks both if it supports and if it's enabled
+ // Checks both if it's supported and if it's enabled; this is thread-safe since its values are
+ // set before onBootFinished, which gates all methods that run on threads other than SF main
virtual bool usePowerHintSession() = 0;
virtual bool supportsPowerHintSession() = 0;
- virtual bool ensurePowerHintSessionRunning() = 0;
// Sends a power hint that updates to the target work duration for the frame
virtual void updateTargetWorkDuration(Duration targetDuration) = 0;
// Sends a power hint for the actual known work duration at the end of the frame
@@ -63,7 +62,7 @@
// Sets whether the power hint session is enabled
virtual void enablePowerHintSession(bool enabled) = 0;
// Initializes the power hint session
- virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
+ virtual bool startPowerHintSession(std::vector<int32_t>&& threadIds) = 0;
// Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
// Reports the start and end times of a hwc validate call this frame for a given display
@@ -94,6 +93,12 @@
virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
// Sets the target duration for the entire pipeline including the gpu
virtual void setTotalFrameTargetWorkDuration(Duration targetDuration) = 0;
+
+ // --- The following methods may run on threads besides SF main ---
+ // Send a hint about an upcoming increase in the CPU workload
+ virtual void notifyCpuLoadUp() = 0;
+ // Send a hint about the imminent start of a new CPU workload
+ virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
};
namespace impl {
@@ -109,16 +114,13 @@
void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
- void notifyCpuLoadUp() override;
- void notifyDisplayUpdateImminentAndCpuReset() override;
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
- bool ensurePowerHintSessionRunning() override;
void updateTargetWorkDuration(Duration targetDuration) override;
void reportActualWorkDuration() override;
void enablePowerHintSession(bool enabled) override;
- bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
- void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
+ bool startPowerHintSession(std::vector<int32_t>&& threadIds) override;
+ void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) override;
void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
TimePoint validateEndTime) override;
void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
@@ -128,13 +130,16 @@
void setExpectedPresentTime(TimePoint expectedPresentTime) override;
void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override;
void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override;
-
void setFrameDelay(Duration frameDelayDuration) override;
void setCommitStart(TimePoint commitStartTime) override;
void setCompositeEnd(TimePoint compositeEndTime) override;
void setDisplays(std::vector<DisplayId>& displayIds) override;
void setTotalFrameTargetWorkDuration(Duration targetDuration) override;
+ // --- The following methods may run on threads besides SF main ---
+ void notifyCpuLoadUp() override;
+ void notifyDisplayUpdateImminentAndCpuReset() override;
+
private:
friend class PowerAdvisorTest;
@@ -220,6 +225,7 @@
// this normalizes them together and takes the max of the two
Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration);
+ bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex);
std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
// Current frame's delay
@@ -242,9 +248,10 @@
// Ensure powerhal connection is initialized
power::PowerHalController& getPowerHal();
+ // These variables are set before mBootFinished and never mutated after, so it's safe to access
+ // from threaded methods.
std::optional<bool> mHintSessionEnabled;
std::optional<bool> mSupportsHintSession;
- bool mHintSessionRunning = false;
std::mutex mHintSessionMutex;
std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
@@ -261,6 +268,11 @@
// The list of thread ids, stored so we can restart the session from this class if needed
std::vector<int32_t> mHintSessionThreadIds;
Duration mLastTargetDurationSent = kDefaultTargetDuration;
+
+ // Used to manage the execution ordering of reportActualWorkDuration for concurrency testing
+ std::promise<bool> mDelayReportActualMutexAcquisitonPromise;
+ bool mTimingTestingMode = false;
+
// Whether we should emit ATRACE_INT data for hint sessions
static const bool sTraceHintSessionData;
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/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/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index aa8d54d..ce59a04 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -630,6 +630,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 +641,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 +1228,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..ce585c6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -293,7 +293,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/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 7379a46..0d79a39 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;
}
@@ -515,6 +521,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/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/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 c4de02f..c56dc83 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -318,6 +318,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
// ---------------------------------------------------------------------------
@@ -724,10 +771,12 @@
}
readPersistentProperties();
- mPowerAdvisor->onBootFinished();
const bool hintSessionEnabled = FlagManager::getInstance().use_adpf_cpu_hint();
mPowerAdvisor->enablePowerHintSession(hintSessionEnabled);
const bool hintSessionUsed = mPowerAdvisor->usePowerHintSession();
+ // Ordering is important here, as onBootFinished signals to PowerAdvisor that concurrency
+ // is safe because its variables are initialized.
+ mPowerAdvisor->onBootFinished();
ALOGD("Power hint is %s",
hintSessionUsed ? "supported" : (hintSessionEnabled ? "unsupported" : "disabled"));
if (hintSessionUsed) {
@@ -737,7 +786,7 @@
if (renderEngineTid.has_value()) {
tidList.emplace_back(*renderEngineTid);
}
- if (!mPowerAdvisor->startPowerHintSession(tidList)) {
+ if (!mPowerAdvisor->startPowerHintSession(std::move(tidList))) {
ALOGW("Cannot start power hint session");
}
}
@@ -2158,19 +2207,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());
+ const char* const whence = __func__;
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 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));
}));
}
}
@@ -2235,8 +2281,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
@@ -2389,8 +2436,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
@@ -2706,7 +2754,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();
@@ -3089,9 +3148,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);
@@ -4060,7 +4119,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;
@@ -4070,14 +4129,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) {
@@ -4360,7 +4459,8 @@
int sampleSize = (layer->getChildrenCount() / 100) + 1;
layer->traverseChildren([&](Layer* layer) {
if (rand() % sampleSize == 0) {
- ALOGE("Child Layer: %s", layer->getName().c_str());
+ ALOGE("Child Layer: %s%s", layer->getName().c_str(),
+ (layer->isHandleAlive() ? "handleAlive" : ""));
}
});
}
@@ -5949,140 +6049,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;
}
@@ -6294,37 +6314,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 {
@@ -6425,7 +6489,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;
@@ -6442,7 +6506,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;
@@ -6459,16 +6526,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);
@@ -8440,17 +8514,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);
@@ -8843,9 +8925,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);
@@ -8914,9 +8996,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);
@@ -9800,13 +9882,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..6b44401 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);
@@ -1080,8 +1109,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 +1132,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,
@@ -1460,6 +1490,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 +1614,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/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index adb4974..cb1faee 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
@@ -160,15 +161,13 @@
"Can't read %s before boot completed as it is server writable", \
__func__); \
} \
- static std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
- static bool value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
+ static const bool value = getFlagValue([] { return flags::name(); }, debugOverride); \
if (mUnitTestMode) { \
/* \
- * When testing, we don't want to rely on the cached values stored in the static \
- * variables. \
+ * When testing, we don't want to rely on the cached `value` or the debugOverride. \
*/ \
- debugOverride = getBoolProperty(syspropOverride); \
- value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ return flags::name(); \
} \
return value; \
}
@@ -201,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 cdab461..2e1d6ae 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/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 88737c1..1a28b81 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"
@@ -123,3 +131,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/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index 803710d..0c820fb 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -89,18 +89,53 @@
EXPECT_DEATH(FlagManager::getInstance().late_boot_misc2(), "");
}
-TEST_F(FlagManagerTest, returnsOverride) {
+TEST_F(FlagManagerTest, returnsOverrideTrue) {
+ mFlagManager.markBootCompleted();
+
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false);
+
+ // This is stored in a static variable, so this test depends on the fact
+ // that this flag has not been read in this process.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true));
+ EXPECT_TRUE(mFlagManager.late_boot_misc2());
+
+ // Further calls will not result in further calls to getBoolProperty.
+ EXPECT_TRUE(mFlagManager.late_boot_misc2());
+}
+
+TEST_F(FlagManagerTest, returnsOverrideReadonly) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::add_sf_skipped_frames_to_trace,
+ false);
+
+ // This is stored in a static variable, so this test depends on the fact
+ // that this flag has not been read in this process.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true));
+ EXPECT_TRUE(mFlagManager.add_sf_skipped_frames_to_trace());
+}
+
+TEST_F(FlagManagerTest, returnsOverrideFalse) {
+ mFlagManager.markBootCompleted();
+
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ refresh_rate_overlay_on_external_display,
+ true);
+
+ // This is stored in a static variable, so this test depends on the fact
+ // that this flag has not been read in this process.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+ EXPECT_FALSE(mFlagManager.refresh_rate_overlay_on_external_display());
+}
+
+TEST_F(FlagManagerTest, ignoresOverrideInUnitTestMode) {
mFlagManager.setUnitTestMode();
- // Twice, since the first call is to initialize the static variable
- EXPECT_CALL(mFlagManager, getBoolProperty)
- .Times((2))
- .WillOnce(Return(true))
- .WillOnce(Return(true));
- EXPECT_EQ(true, mFlagManager.late_boot_misc2());
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::multithreaded_present, true);
- EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
- EXPECT_EQ(false, mFlagManager.late_boot_misc2());
+ // If this has not been called in this process, it will be called.
+ // Regardless, the result is ignored.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(false));
+
+ EXPECT_EQ(true, mFlagManager.multithreaded_present());
}
TEST_F(FlagManagerTest, returnsValue) {
@@ -119,20 +154,6 @@
}
}
-TEST_F(FlagManagerTest, readonlyReturnsOverride) {
- mFlagManager.setUnitTestMode();
-
- // Twice, since the first call is to initialize the static variable
- EXPECT_CALL(mFlagManager, getBoolProperty)
- .Times(2)
- .WillOnce(Return(true))
- .WillOnce(Return(true));
- EXPECT_EQ(true, mFlagManager.misc1());
-
- EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
- EXPECT_EQ(false, mFlagManager.misc1());
-}
-
TEST_F(FlagManagerTest, readonlyReturnsValue) {
mFlagManager.setUnitTestMode();
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/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/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 85f66f4..9c66a97 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -24,6 +24,7 @@
#include <powermanager/PowerHalWrapper.h>
#include <ui/DisplayId.h>
#include <chrono>
+#include <future>
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockIPowerHintSession.h"
#include "mock/DisplayHardware/MockPowerHalController.h"
@@ -40,11 +41,14 @@
class PowerAdvisorTest : public testing::Test {
public:
void SetUp() override;
- void startPowerHintSession();
+ void startPowerHintSession(bool returnValidSession = true);
void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod);
void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime);
Duration getFenceWaitDelayDuration(bool skipValidate);
Duration getErrorMargin();
+ void setTimingTestingMode(bool testinMode);
+ void allowReportActualToAcquireMutex();
+ bool sessionExists();
protected:
TestableSurfaceFlinger mFlinger;
@@ -53,6 +57,11 @@
std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession;
};
+bool PowerAdvisorTest::sessionExists() {
+ std::scoped_lock lock(mPowerAdvisor->mHintSessionMutex);
+ return mPowerAdvisor->mHintSession != nullptr;
+}
+
void PowerAdvisorTest::SetUp() {
mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
@@ -62,14 +71,20 @@
.WillByDefault(Return(HalResult<int64_t>::fromStatus(binder::Status::ok(), 16000)));
}
-void PowerAdvisorTest::startPowerHintSession() {
- const std::vector<int32_t> threadIds = {1, 2, 3};
+void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) {
mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
- ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(binder::Status::ok(), mMockPowerHintSession)));
+ if (returnValidSession) {
+ ON_CALL(*mMockPowerHalController, createHintSession)
+ .WillByDefault(
+ Return(HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(binder::Status::ok(), mMockPowerHintSession)));
+ } else {
+ ON_CALL(*mMockPowerHalController, createHintSession)
+ .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(binder::Status::ok(), nullptr)));
+ }
mPowerAdvisor->enablePowerHintSession(true);
- mPowerAdvisor->startPowerHintSession(threadIds);
+ mPowerAdvisor->startPowerHintSession({1, 2, 3});
ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
.WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
@@ -86,6 +101,14 @@
mPowerAdvisor->updateTargetWorkDuration(vsyncPeriod);
}
+void PowerAdvisorTest::setTimingTestingMode(bool testingMode) {
+ mPowerAdvisor->mTimingTestingMode = testingMode;
+}
+
+void PowerAdvisorTest::allowReportActualToAcquireMutex() {
+ mPowerAdvisor->mDelayReportActualMutexAcquisitonPromise.set_value(true);
+}
+
Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
: PowerAdvisor::kFenceWaitStartDelayValidated);
@@ -221,5 +244,131 @@
mPowerAdvisor->reportActualWorkDuration();
}
+TEST_F(PowerAdvisorTest, hintSessionValidWhenNullFromPowerHAL) {
+ mPowerAdvisor->onBootFinished();
+
+ startPowerHintSession(false);
+
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+ // 60hz
+ const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+ const Duration presentDuration = 5ms;
+ const Duration postCompDuration = 1ms;
+
+ TimePoint startTime{100ns};
+
+ // advisor only starts on frame 2 so do an initial no-op frame
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+ // increment the frame
+ startTime += vsyncPeriod;
+
+ const Duration expectedDuration = getErrorMargin() + presentDuration + postCompDuration;
+ EXPECT_CALL(*mMockPowerHintSession,
+ reportActualWorkDuration(ElementsAre(
+ Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
+ .Times(0);
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->reportActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) {
+ EXPECT_CALL(*mMockPowerHalController, createHintSession(_, _, _, _)).Times(1);
+ mPowerAdvisor->onBootFinished();
+ startPowerHintSession();
+ mPowerAdvisor->startPowerHintSession({1, 2, 3});
+}
+
+TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) {
+ // notifyDisplayUpdateImminentAndCpuReset or notifyCpuLoadUp gets called in background
+ // reportActual gets called during callback and sees true session, passes ensure
+ // first notify finishes, setting value to true. Another async method gets called, acquires the
+ // lock between reportactual finishing ensure and acquiring the lock itself, and sets session to
+ // nullptr. reportActual acquires the lock, and the session is now null, so it does nullptr
+ // deref
+
+ mPowerAdvisor->onBootFinished();
+ startPowerHintSession();
+
+ // --- fake a bunch of timing data
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+ // 60hz
+ const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+ const Duration presentDuration = 5ms;
+ const Duration postCompDuration = 1ms;
+ TimePoint startTime{100ns};
+ // advisor only starts on frame 2 so do an initial no-op frame
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+ // increment the frame
+ startTime += vsyncPeriod;
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ // --- Done faking timing data
+
+ setTimingTestingMode(true);
+ std::promise<bool> letSendHintFinish;
+
+ ON_CALL(*mMockPowerHintSession, sendHint).WillByDefault([&letSendHintFinish] {
+ letSendHintFinish.get_future().wait();
+ return ndk::ScopedAStatus::fromExceptionCode(-127);
+ });
+
+ ON_CALL(*mMockPowerHintSession, reportActualWorkDuration).WillByDefault([] {
+ return ndk::ScopedAStatus::fromExceptionCode(-127);
+ });
+
+ ON_CALL(*mMockPowerHalController, createHintSession)
+ .WillByDefault(Return(
+ HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr)));
+
+ // First background call, to notice the session is down
+ auto firstHint = std::async(std::launch::async, [this] {
+ mPowerAdvisor->notifyCpuLoadUp();
+ return true;
+ });
+ std::this_thread::sleep_for(10ms);
+
+ // Call reportActual while callback is resolving to try and sneak past ensure
+ auto reportActual =
+ std::async(std::launch::async, [this] { mPowerAdvisor->reportActualWorkDuration(); });
+
+ std::this_thread::sleep_for(10ms);
+ // Let the first call finish
+ letSendHintFinish.set_value(true);
+ letSendHintFinish = std::promise<bool>{};
+ firstHint.wait();
+
+ // Do the second notify call, to ensure the session is nullptr
+ auto secondHint = std::async(std::launch::async, [this] {
+ mPowerAdvisor->notifyCpuLoadUp();
+ return true;
+ });
+ letSendHintFinish.set_value(true);
+ secondHint.wait();
+ // Let report finish, potentially dereferencing
+ allowReportActualToAcquireMutex();
+ reportActual.wait();
+ EXPECT_EQ(sessionExists(), false);
+}
+
} // namespace
} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 94d517a..b620830 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -110,7 +110,7 @@
EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), display.isPrimary());
std::optional<DisplayDeviceState::Physical> expectedPhysical;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ if (Case::Display::CONNECTION_TYPE::value) {
const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
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..22cb24b 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -695,6 +695,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/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/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/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index d635508..8e8eb1d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -36,11 +36,10 @@
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
- MOCK_METHOD(bool, ensurePowerHintSessionRunning, (), (override));
MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
MOCK_METHOD(void, reportActualWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
- MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
MOCK_METHOD(void, setGpuFenceTime,
(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
MOCK_METHOD(void, setHwcValidateTiming,
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/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 7c8e695..3e3f962 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 */
+ 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"