Merge "Clip blur to bounds of layer"
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index 7026ca8..491c629 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -23,4 +23,6 @@
METADATA_WINDOW_TYPE = 2,
METADATA_TASK_ID = 3,
METADATA_MOUSE_CURSOR = 4,
+ METADATA_ACCESSIBILITY_ID = 5,
+ METADATA_OWNER_PID = 6,
}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 5076ae6..f583c9b 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -450,6 +450,20 @@
AddArg("--boot-image-merge");
}
+ uint32_t min_new_classes_percent_change = ::android::base::GetUintProperty<uint32_t>(
+ "dalvik.vm.bgdexopt.new-classes-percent", /*default*/-1);
+ if (min_new_classes_percent_change >= 0 && min_new_classes_percent_change <= 100) {
+ AddArg("--min-new-classes-percent-change=" +
+ std::to_string(min_new_classes_percent_change));
+ }
+
+ uint32_t min_new_methods_percent_change = ::android::base::GetUintProperty<uint32_t>(
+ "dalvik.vm.bgdexopt.new-methods-percent", /*default*/-1);
+ if (min_new_methods_percent_change >=0 && min_new_methods_percent_change <= 100) {
+ AddArg("--min-new-methods-percent-change=" +
+ std::to_string(min_new_methods_percent_change));
+ }
+
// Do not add after dex2oat_flags, they should override others for debugging.
PrepareArgs(profman_bin);
}
diff --git a/data/etc/cec_config.xml b/data/etc/cec_config.xml
index 42a45e4..5defb2f 100644
--- a/data/etc/cec_config.xml
+++ b/data/etc/cec_config.xml
@@ -1,14 +1,25 @@
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<cec-settings>
<setting name="hdmi_cec_enabled"
+ value-type="int"
user-configurable="true">
<allowed-values>
- <value string-value="0" />
- <value string-value="1" />
+ <value int-value="0" />
+ <value int-value="1" />
</allowed-values>
- <default-value string-value="1" />
+ <default-value int-value="1" />
+ </setting>
+ <setting name="hdmi_cec_version"
+ value-type="int"
+ user-configurable="true">
+ <allowed-values>
+ <value int-value="0x05" />
+ <value int-value="0x06" />
+ </allowed-values>
+ <default-value int-value="0x05" />
</setting>
<setting name="send_standby_on_sleep"
+ value-type="string"
user-configurable="true">
<allowed-values>
<value string-value="to_tv" />
@@ -18,6 +29,7 @@
<default-value string-value="to_tv" />
</setting>
<setting name="power_state_change_on_active_source_lost"
+ value-type="string"
user-configurable="false">
<allowed-values>
<value string-value="none" />
@@ -26,11 +38,12 @@
<default-value string-value="none" />
</setting>
<setting name="system_audio_mode_muting"
+ value-type="int"
user-configurable="false">
<allowed-values>
- <value string-value="0" />
- <value string-value="1" />
+ <value int-value="0" />
+ <value int-value="1" />
</allowed-values>
- <default-value string-value="1" />
+ <default-value int-value="1" />
</setting>
</cec-settings>
diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h
new file mode 100644
index 0000000..7817126
--- /dev/null
+++ b/include/android/permission_manager.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PERMISSION_MANAGER_H
+#define ANDROID_PERMISSION_MANAGER_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/**
+ * Permission check results.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * This is returned by APermissionManager_checkPermission()
+ * if the permission has been granted to the given package.
+ */
+ PERMISSION_MANAGER_PERMISSION_GRANTED = 0,
+ /**
+ * This is returned by APermissionManager_checkPermission()
+ * if the permission has not been granted to the given package.
+ */
+ PERMISSION_MANAGER_PERMISSION_DENIED = -1,
+};
+
+/**
+ * Permission check return status values.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * This is returned if the permission check completed without errors.
+ * The output result is valid and contains one of {PERMISSION_MANAGER_PERMISSION_GRANTED,
+ * PERMISSION_MANAGER_PERMISSION_DENIED}.
+ */
+ PERMISSION_MANAGER_STATUS_OK = 0,
+ /**
+ * This is returned if the permission check encountered an unspecified error.
+ * The output result is unmodified.
+ */
+ PERMISSION_MANAGER_STATUS_ERROR_UNKNOWN = -1,
+ /**
+ * This is returned if the permission check failed because the service is
+ * unavailable. The output result is unmodified.
+ */
+ PERMISSION_MANAGER_STATUS_SERVICE_UNAVAILABLE = -2,
+};
+
+#if __ANDROID_API__ >= 31
+
+/**
+ * Checks whether the package with the given pid/uid has been granted a permission.
+ *
+ * Note that the Java API of Context#checkPermission() is usually faster due to caching,
+ * thus is preferred over this API wherever possible.
+ *
+ * @param permission the permission to be checked.
+ * @param pid the process id of the package to be checked.
+ * @param uid the uid of the package to be checked.
+ * @param outResult output of the permission check result.
+ *
+ * @return error codes if any error happened during the check.
+ */
+int32_t APermissionManager_checkPermission(const char* permission,
+ pid_t pid,
+ uid_t uid,
+ int32_t* outResult) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
+__END_DECLS
+
+#endif // ANDROID_PERMISSION_MANAGER_H
diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h
new file mode 100644
index 0000000..28f717a
--- /dev/null
+++ b/include/ftl/ArrayTraits.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2020 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 <algorithm>
+#include <iterator>
+#include <new>
+
+#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
+
+namespace android::ftl {
+
+template <typename T>
+struct ArrayTraits {
+ using value_type = T;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ using pointer = value_type*;
+ using reference = value_type&;
+ using iterator = pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+
+ using const_pointer = const value_type*;
+ using const_reference = const value_type&;
+ using const_iterator = const_pointer;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // TODO: Replace with std::construct_at in C++20.
+ template <typename... Args>
+ static pointer construct_at(const_iterator it, Args&&... args) {
+ void* const ptr = const_cast<void*>(static_cast<const void*>(it));
+ return new (ptr) value_type{std::forward<Args>(args)...};
+ }
+};
+
+// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
+template <typename Self, typename T>
+class ArrayIterators {
+ FTL_ARRAY_TRAIT(T, size_type);
+
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); }
+
+public:
+ const_iterator begin() const { return cbegin(); }
+ const_iterator cbegin() const { return self().begin(); }
+
+ const_iterator end() const { return cend(); }
+ const_iterator cend() const { return self().end(); }
+
+ reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); }
+ const_reverse_iterator rbegin() const { return crbegin(); }
+ const_reverse_iterator crbegin() const { return self().rbegin(); }
+
+ reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); }
+ const_reverse_iterator rend() const { return crend(); }
+ const_reverse_iterator crend() const { return self().rend(); }
+
+ iterator last() { return self().end() - 1; }
+ const_iterator last() const { return self().last(); }
+
+ reference front() { return *self().begin(); }
+ const_reference front() const { return self().front(); }
+
+ reference back() { return *last(); }
+ const_reference back() const { return self().back(); }
+
+ reference operator[](size_type i) { return *(self().begin() + i); }
+ const_reference operator[](size_type i) const { return self()[i]; }
+};
+
+// Mixin to define comparison operators for an array-like template.
+// TODO: Replace with operator<=> in C++20.
+template <template <typename, size_t> class Array>
+struct ArrayComparators {
+ template <typename T, size_t N, size_t M>
+ friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return rhs < lhs;
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs == rhs);
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs < rhs);
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs > rhs);
+ }
+};
+
+} // namespace android::ftl
diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h
new file mode 100644
index 0000000..cecec7f
--- /dev/null
+++ b/include/ftl/SmallVector.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2020 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 <ftl/ArrayTraits.h>
+#include <ftl/StaticVector.h>
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace android::ftl {
+
+template <typename>
+struct IsSmallVector;
+
+// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement
+// for std::vector with statically allocated storage for N elements, whose goal is to improve run
+// time by avoiding heap allocation and increasing probability of cache hits. The standard API is
+// augmented by an unstable_erase operation that does not preserve order, and a replace operation
+// that destructively emplaces.
+//
+// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
+//
+// Example usage:
+//
+// ftl::SmallVector<char, 3> vector;
+// assert(vector.empty());
+// assert(!vector.dynamic());
+//
+// vector = {'a', 'b', 'c'};
+// assert(vector.size() == 3u);
+// assert(!vector.dynamic());
+//
+// vector.push_back('d');
+// assert(vector.dynamic());
+//
+// vector.unstable_erase(vector.begin());
+// assert(vector == (ftl::SmallVector{'d', 'b', 'c'}));
+//
+// vector.pop_back();
+// assert(vector.back() == 'b');
+// assert(vector.dynamic());
+//
+// const char array[] = "hi";
+// vector = ftl::SmallVector(array);
+// assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
+// assert(!vector.dynamic());
+//
+template <typename T, size_t N>
+class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
+ using Static = StaticVector<T, N>;
+ using Dynamic = SmallVector<T, 0>;
+
+ // TODO: Replace with std::remove_cvref_t in C++20.
+ template <typename U>
+ using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+
+public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ // Creates an empty vector.
+ SmallVector() = default;
+
+ // Constructs at most N elements. See StaticVector for underlying constructors.
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<!IsSmallVector<remove_cvref_t<Arg>>{}>>
+ SmallVector(Arg&& arg, Args&&... args)
+ : mVector(std::in_place_type<Static>, std::forward<Arg>(arg),
+ std::forward<Args>(args)...) {}
+
+ // Copies at most N elements from a smaller convertible vector.
+ template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
+ SmallVector(const SmallVector<U, M>& other)
+ : SmallVector(IteratorRange, other.begin(), other.end()) {}
+
+ void swap(SmallVector& other) { mVector.swap(other.mVector); }
+
+ // Returns whether the vector is backed by static or dynamic storage.
+ bool dynamic() const { return std::holds_alternative<Dynamic>(mVector); }
+
+ // Avoid std::visit as it generates a dispatch table.
+#define DISPATCH(T, F, ...) \
+ T F() __VA_ARGS__ { \
+ return dynamic() ? std::get<Dynamic>(mVector).F() : std::get<Static>(mVector).F(); \
+ }
+
+ DISPATCH(size_type, max_size, const)
+ DISPATCH(size_type, size, const)
+ DISPATCH(bool, empty, const)
+
+ // noexcept to suppress warning about zero variadic macro arguments.
+ DISPATCH(iterator, begin, noexcept)
+ DISPATCH(const_iterator, begin, const)
+ DISPATCH(const_iterator, cbegin, const)
+
+ DISPATCH(iterator, end, noexcept)
+ DISPATCH(const_iterator, end, const)
+ DISPATCH(const_iterator, cend, const)
+
+ DISPATCH(reverse_iterator, rbegin, noexcept)
+ DISPATCH(const_reverse_iterator, rbegin, const)
+ DISPATCH(const_reverse_iterator, crbegin, const)
+
+ DISPATCH(reverse_iterator, rend, noexcept)
+ DISPATCH(const_reverse_iterator, rend, const)
+ DISPATCH(const_reverse_iterator, crend, const)
+
+ DISPATCH(iterator, last, noexcept)
+ DISPATCH(const_iterator, last, const)
+
+ DISPATCH(reference, front, noexcept)
+ DISPATCH(const_reference, front, const)
+
+ DISPATCH(reference, back, noexcept)
+ DISPATCH(const_reference, back, const)
+
+#undef DISPATCH
+
+ reference operator[](size_type i) {
+ return dynamic() ? std::get<Dynamic>(mVector)[i] : std::get<Static>(mVector)[i];
+ }
+
+ const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; }
+
+ // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+ // replacing at end() is erroneous.
+ //
+ // The element is emplaced via move constructor, so type T does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the element being replaced.
+ //
+ // Iterators to the replaced element point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ if (dynamic()) {
+ return std::get<Dynamic>(mVector).replace(it, std::forward<Args>(args)...);
+ } else {
+ return std::get<Static>(mVector).replace(it, std::forward<Args>(args)...);
+ }
+ }
+
+ // Appends an element, and returns a reference to it.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ template <typename... Args>
+ reference emplace_back(Args&&... args) {
+ constexpr auto insertStatic = &Static::template emplace_back<Args...>;
+ constexpr auto insertDynamic = &Dynamic::template emplace_back<Args...>;
+ return *insert<insertStatic, insertDynamic>(std::forward<Args>(args)...);
+ }
+
+ // Appends an element.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ void push_back(const value_type& v) {
+ constexpr auto insertStatic =
+ static_cast<bool (Static::*)(const value_type&)>(&Static::push_back);
+ constexpr auto insertDynamic =
+ static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back);
+ insert<insertStatic, insertDynamic>(v);
+ }
+
+ void push_back(value_type&& v) {
+ constexpr auto insertStatic =
+ static_cast<bool (Static::*)(value_type&&)>(&Static::push_back);
+ constexpr auto insertDynamic =
+ static_cast<bool (Dynamic::*)(value_type&&)>(&Dynamic::push_back);
+ insert<insertStatic, insertDynamic>(std::move(v));
+ }
+
+ // Removes the last element. The vector must not be empty, or the call is erroneous.
+ //
+ // The last() and end() iterators are invalidated.
+ //
+ void pop_back() {
+ if (dynamic()) {
+ std::get<Dynamic>(mVector).pop_back();
+ } else {
+ std::get<Static>(mVector).pop_back();
+ }
+ }
+
+ // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+ // this moves the last element to the slot of the erased element.
+ //
+ // The last() and end() iterators, as well as those to the erased element, are invalidated.
+ //
+ void unstable_erase(iterator it) {
+ if (dynamic()) {
+ std::get<Dynamic>(mVector).unstable_erase(it);
+ } else {
+ std::get<Static>(mVector).unstable_erase(it);
+ }
+ }
+
+private:
+ template <auto insertStatic, auto insertDynamic, typename... Args>
+ auto insert(Args&&... args) {
+ if (Dynamic* const vector = std::get_if<Dynamic>(&mVector)) {
+ return (vector->*insertDynamic)(std::forward<Args>(args)...);
+ }
+
+ auto& vector = std::get<Static>(mVector);
+ if (vector.full()) {
+ return (promote(vector).*insertDynamic)(std::forward<Args>(args)...);
+ } else {
+ return (vector.*insertStatic)(std::forward<Args>(args)...);
+ }
+ }
+
+ Dynamic& promote(Static& staticVector) {
+ assert(staticVector.full());
+
+ // Allocate double capacity to reduce probability of reallocation.
+ Dynamic vector;
+ vector.reserve(Static::max_size() * 2);
+ std::move(staticVector.begin(), staticVector.end(), std::back_inserter(vector));
+
+ return mVector.template emplace<Dynamic>(std::move(vector));
+ }
+
+ std::variant<Static, Dynamic> mVector;
+};
+
+// Partial specialization without static storage.
+template <typename T>
+class SmallVector<T, 0> final : ArrayTraits<T>,
+ ArrayIterators<SmallVector<T, 0>, T>,
+ std::vector<T> {
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<SmallVector, T>;
+ using Impl = std::vector<T>;
+
+ friend Iter;
+
+public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ using Impl::Impl;
+
+ using Impl::empty;
+ using Impl::max_size;
+ using Impl::size;
+
+ using Impl::reserve;
+
+ // std::vector iterators are not necessarily raw pointers.
+ iterator begin() { return Impl::data(); }
+ iterator end() { return Impl::data() + size(); }
+
+ using Iter::begin;
+ using Iter::end;
+
+ using Iter::cbegin;
+ using Iter::cend;
+
+ using Iter::rbegin;
+ using Iter::rend;
+
+ using Iter::crbegin;
+ using Iter::crend;
+
+ using Iter::last;
+
+ using Iter::back;
+ using Iter::front;
+
+ using Iter::operator[];
+
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ value_type element{std::forward<Args>(args)...};
+ std::destroy_at(it);
+ // This is only safe because exceptions are disabled.
+ return *construct_at(it, std::move(element));
+ }
+
+ template <typename... Args>
+ iterator emplace_back(Args&&... args) {
+ return &Impl::emplace_back(std::forward<Args>(args)...);
+ }
+
+ bool push_back(const value_type& v) {
+ Impl::push_back(v);
+ return true;
+ }
+
+ bool push_back(value_type&& v) {
+ Impl::push_back(std::move(v));
+ return true;
+ }
+
+ using Impl::pop_back;
+
+ void unstable_erase(iterator it) {
+ if (it != last()) std::iter_swap(it, last());
+ pop_back();
+ }
+
+ void swap(SmallVector& other) { Impl::swap(other); }
+};
+
+template <typename>
+struct IsSmallVector : std::false_type {};
+
+template <typename T, size_t N>
+struct IsSmallVector<SmallVector<T, N>> : std::true_type {};
+
+// Deduction guide for array constructor.
+template <typename T, size_t N>
+SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+ typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, typename... Us>
+SmallVector(std::in_place_type_t<T>, Us&&...) -> SmallVector<T, sizeof...(Us)>;
+
+// Deduction guide for StaticVector conversion.
+template <typename T, size_t N>
+SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>;
+
+template <typename T, size_t N>
+inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h
index b586e91..457095d 100644
--- a/include/ftl/StaticVector.h
+++ b/include/ftl/StaticVector.h
@@ -16,22 +16,25 @@
#pragma once
+#include <ftl/ArrayTraits.h>
+
#include <algorithm>
#include <cassert>
#include <iterator>
#include <memory>
-#include <new>
#include <type_traits>
#include <utility>
namespace android::ftl {
+constexpr struct IteratorRangeTag {} IteratorRange;
+
// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
// default constructor, since elements are constructed in-place as the vector grows. Operations that
-// insert an element, such as push_back and emplace, fail when the vector is full. The API otherwise
-// adheres to standard containers, except the unstable_erase operation that does not shift elements,
+// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
+// adheres to standard containers, except the unstable_erase operation that does not preserve order,
// and the replace operation that destructively emplaces.
//
// StaticVector<T, 1> is analogous to an iterable std::optional, but StaticVector<T, 0> is an error.
@@ -61,46 +64,70 @@
// assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
//
template <typename T, size_t N>
-class StaticVector {
+class StaticVector final : ArrayTraits<T>,
+ ArrayIterators<StaticVector<T, N>, T>,
+ ArrayComparators<StaticVector> {
static_assert(N > 0);
- template <typename I>
- using IsInputIterator = std::is_base_of<std::input_iterator_tag,
- typename std::iterator_traits<I>::iterator_category>;
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<StaticVector, T>;
+ friend Iter;
+
+ // There is ambiguity when constructing from two iterator-like elements like pointers:
+ // they could be an iterator range, or arguments for in-place construction. Assume the
+ // latter unless they are input iterators and cannot be used to construct elements. If
+ // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
+ template <typename I, typename Traits = std::iterator_traits<I>>
+ using IsInputIterator = std::conjunction<
+ std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
+ std::negation<std::is_constructible<T, I>>>;
public:
- using value_type = T;
- using size_type = size_t;
- using difference_type = ptrdiff_t;
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
- using pointer = value_type*;
- using reference = value_type&;
- using iterator = pointer;
- using reverse_iterator = std::reverse_iterator<iterator>;
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
- using const_pointer = const value_type*;
- using const_reference = const value_type&;
- using const_iterator = const_pointer;
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
// Creates an empty vector.
StaticVector() = default;
// Copies and moves a vector, respectively.
- StaticVector(const StaticVector& other) : StaticVector(other.begin(), other.end()) {}
+ StaticVector(const StaticVector& other)
+ : StaticVector(IteratorRange, other.begin(), other.end()) {}
StaticVector(StaticVector&& other) { swap<Empty>(other); }
// Copies at most N elements from a smaller convertible vector.
template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
- StaticVector(const StaticVector<U, M>& other) : StaticVector(other.begin(), other.end()) {}
+ StaticVector(const StaticVector<U, M>& other)
+ : StaticVector(IteratorRange, other.begin(), other.end()) {}
// Copies at most N elements from an array.
template <typename U, size_t M>
- explicit StaticVector(U (&array)[M]) : StaticVector(std::begin(array), std::end(array)) {}
+ explicit StaticVector(U (&array)[M])
+ : StaticVector(IteratorRange, std::begin(array), std::end(array)) {}
// Copies at most N elements from the range [first, last).
+ //
+ // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
+ //
template <typename Iterator, typename = std::enable_if_t<IsInputIterator<Iterator>{}>>
- StaticVector(Iterator first, Iterator last)
+ StaticVector(Iterator first, Iterator last) : StaticVector(IteratorRange, first, last) {
+ using V = typename std::iterator_traits<Iterator>::value_type;
+ static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
+ }
+
+ template <typename Iterator>
+ StaticVector(IteratorRangeTag, Iterator first, Iterator last)
: mSize(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
std::uninitialized_copy(first, first + mSize, begin());
}
@@ -154,53 +181,57 @@
template <typename = void>
void swap(StaticVector&);
- size_type max_size() const { return N; }
+ static constexpr size_type max_size() { return N; }
size_type size() const { return mSize; }
bool empty() const { return size() == 0; }
bool full() const { return size() == max_size(); }
iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); }
- const_iterator begin() const { return cbegin(); }
- const_iterator cbegin() const { return mut().begin(); }
-
iterator end() { return begin() + size(); }
- const_iterator end() const { return cend(); }
- const_iterator cend() const { return mut().end(); }
- reverse_iterator rbegin() { return std::make_reverse_iterator(end()); }
- const_reverse_iterator rbegin() const { return crbegin(); }
- const_reverse_iterator crbegin() const { return mut().rbegin(); }
+ using Iter::begin;
+ using Iter::end;
- reverse_iterator rend() { return std::make_reverse_iterator(begin()); }
- const_reverse_iterator rend() const { return crend(); }
- const_reverse_iterator crend() const { return mut().rend(); }
+ using Iter::cbegin;
+ using Iter::cend;
- iterator last() { return end() - 1; }
- const_iterator last() const { return mut().last(); }
+ using Iter::rbegin;
+ using Iter::rend;
- reference front() { return *begin(); }
- const_reference front() const { return mut().front(); }
+ using Iter::crbegin;
+ using Iter::crend;
- reference back() { return *last(); }
- const_reference back() const { return mut().back(); }
+ using Iter::last;
- reference operator[](size_type i) { return *(begin() + i); }
- const_reference operator[](size_type i) const { return mut()[i]; }
+ using Iter::back;
+ using Iter::front;
- // Replaces an element, and returns an iterator to it. If the vector is full, the element is not
- // replaced, and the end iterator is returned.
+ using Iter::operator[];
+
+ // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+ // replacing at end() is erroneous.
+ //
+ // The element is emplaced via move constructor, so type T does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the element being replaced.
+ //
+ // Iterators to the replaced element point to its replacement, and others remain valid.
+ //
template <typename... Args>
- iterator replace(const_iterator cit, Args&&... args) {
- if (full()) return end();
- // Append element, and move into place if not last.
- emplace_back(std::forward<Args>(args)...);
- if (cit != last()) unstable_erase(cit);
- return const_cast<iterator>(cit);
+ reference replace(const_iterator it, Args&&... args) {
+ value_type element{std::forward<Args>(args)...};
+ std::destroy_at(it);
+ // This is only safe because exceptions are disabled.
+ return *construct_at(it, std::move(element));
}
// Appends an element, and returns an iterator to it. If the vector is full, the element is not
- // inserted, and the end iterator is returned.
+ // inserted, and the end() iterator is returned.
+ //
+ // On success, the end() iterator is invalidated.
+ //
template <typename... Args>
iterator emplace_back(Args&&... args) {
if (full()) return end();
@@ -209,31 +240,47 @@
return it;
}
+ // Appends an element unless the vector is full, and returns whether the element was inserted.
+ //
+ // On success, the end() iterator is invalidated.
+ //
+ bool push_back(const value_type& v) {
+ // Two statements for sequence point.
+ const iterator it = emplace_back(v);
+ return it != end();
+ }
+
+ bool push_back(value_type&& v) {
+ // Two statements for sequence point.
+ const iterator it = emplace_back(std::move(v));
+ return it != end();
+ }
+
+ // Removes the last element. The vector must not be empty, or the call is erroneous.
+ //
+ // The last() and end() iterators are invalidated.
+ //
+ void pop_back() { unstable_erase(last()); }
+
// Erases an element, but does not preserve order. Rather than shifting subsequent elements,
// this moves the last element to the slot of the erased element.
+ //
+ // The last() and end() iterators, as well as those to the erased element, are invalidated.
+ //
void unstable_erase(const_iterator it) {
std::destroy_at(it);
if (it != last()) {
- // Move last element and destroy its source for destructor side effects.
+ // Move last element and destroy its source for destructor side effects. This is only
+ // safe because exceptions are disabled.
construct_at(it, std::move(back()));
std::destroy_at(last());
}
--mSize;
}
- bool push_back(value_type v) {
- // Two statements for sequence point.
- const iterator it = emplace_back(std::move(v));
- return it != end();
- }
-
- void pop_back() { unstable_erase(last()); }
-
private:
struct Empty {};
- StaticVector& mut() const { return *const_cast<StaticVector*>(this); }
-
// Recursion for variadic constructor.
template <size_t I, typename E, typename... Es>
StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
@@ -245,13 +292,6 @@
template <size_t I>
explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
- // TODO: Replace with std::construct_at in C++20.
- template <typename... Args>
- static pointer construct_at(const_iterator it, Args&&... args) {
- void* const ptr = const_cast<void*>(static_cast<const void*>(it));
- return new (ptr) value_type{std::forward<Args>(args)...};
- }
-
size_type mSize = 0;
std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
};
@@ -307,35 +347,4 @@
lhs.swap(rhs);
}
-// TODO: Replace with operator<=> in C++20.
-template <typename T, size_t N, size_t M>
-inline bool operator==(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator<(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator>(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return rhs < lhs;
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator!=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return !(lhs == rhs);
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator>=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return !(lhs < rhs);
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator<=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return !(rhs < lhs);
-}
-
} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 334fe34..b90d57e 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -19,6 +19,7 @@
#include <android-base/stringprintf.h>
#include <input/Input.h>
+#include <input/NamedEnum.h>
#include <cinttypes>
#include <optional>
@@ -44,18 +45,6 @@
VIRTUAL = 3,
};
-static const char* viewportTypeToString(ViewportType type) {
- switch (type) {
- case ViewportType::INTERNAL:
- return "INTERNAL";
- case ViewportType::EXTERNAL:
- return "EXTERNAL";
- case ViewportType::VIRTUAL:
- return "VIRTUAL";
- }
- return "UNKNOWN";
-}
-
/*
* Describes how coordinates are mapped on a physical display.
* See com.android.server.display.DisplayViewport.
@@ -142,7 +131,7 @@
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d], "
"isActive=[%d]",
- viewportTypeToString(type), displayId, uniqueId.c_str(),
+ NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
: "<none>",
orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
diff --git a/include/input/Input.h b/include/input/Input.h
index d3a9694..3facfa5 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -177,7 +177,9 @@
namespace android {
+#ifdef __linux__
class Parcel;
+#endif
const char* inputEventTypeToString(int32_t type);
@@ -344,8 +346,10 @@
return getAxisValue(AMOTION_EVENT_AXIS_Y);
}
+#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
+#endif
bool operator==(const PointerCoords& other) const;
inline bool operator!=(const PointerCoords& other) const {
@@ -704,8 +708,10 @@
// Matrix is in row-major form and compatible with SkMatrix.
void transform(const std::array<float, 9>& matrix);
+#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
+#endif
static bool isTouchEvent(uint32_t source, int32_t action);
inline bool isTouchEvent() const {
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 339c7eb..23f8ddf 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -19,7 +19,9 @@
#include <stdint.h>
+#ifdef __linux__
#include <binder/IBinder.h>
+#endif
#include <android-base/result.h>
#include <input/Input.h>
@@ -42,27 +44,27 @@
*/
class KeyCharacterMap {
public:
- enum KeyboardType {
- KEYBOARD_TYPE_UNKNOWN = 0,
- KEYBOARD_TYPE_NUMERIC = 1,
- KEYBOARD_TYPE_PREDICTIVE = 2,
- KEYBOARD_TYPE_ALPHA = 3,
- KEYBOARD_TYPE_FULL = 4,
+ enum class KeyboardType : int32_t {
+ UNKNOWN = 0,
+ NUMERIC = 1,
+ PREDICTIVE = 2,
+ ALPHA = 3,
+ FULL = 4,
/**
* Deprecated. Set 'keyboard.specialFunction' to '1' in the device's IDC file instead.
*/
- KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
- KEYBOARD_TYPE_OVERLAY = 6,
+ SPECIAL_FUNCTION = 5,
+ OVERLAY = 6,
};
- enum Format {
+ enum class Format {
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
- FORMAT_BASE = 0,
+ BASE = 0,
// Overlay keyboard layout, more restrictive, may be published by applications,
// cannot override device-specific options.
- FORMAT_OVERLAY = 1,
+ OVERLAY = 1,
// Either base or overlay layout ok.
- FORMAT_ANY = 2,
+ ANY = 2,
};
// Substitute key code and meta state for fallback action.
@@ -86,7 +88,7 @@
void combine(const KeyCharacterMap& overlay);
/* Gets the keyboard type. */
- int32_t getKeyboardType() const;
+ KeyboardType getKeyboardType() const;
/* Gets the primary character for this key as in the label physically printed on it.
* Returns 0 if none (eg. for non-printing keys). */
@@ -132,11 +134,13 @@
void tryRemapKey(int32_t scanCode, int32_t metaState,
int32_t* outKeyCode, int32_t* outMetaState) const;
+#ifdef __linux__
/* Reads a key map from a parcel. */
static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
/* Writes a key map to a parcel. */
void writeToParcel(Parcel* parcel) const;
+#endif
KeyCharacterMap(const KeyCharacterMap& other);
@@ -222,7 +226,7 @@
};
KeyedVector<int32_t, Key*> mKeys;
- int mType;
+ KeyboardType mType;
std::string mLoadFileName;
KeyedVector<int32_t, int32_t> mKeysByScanCode;
diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h
index 42cfb12..1d987fe 100644
--- a/include/input/NamedEnum.h
+++ b/include/input/NamedEnum.h
@@ -115,10 +115,10 @@
// Do not specialize it to a large number to avoid performance issues.
// The recommended maximum enum number to specialize is 64.
template <typename E>
- static const std::string string(E val) {
+ static const std::string string(E val, const char* fallbackFormat = "0x%08x") {
std::string result;
std::optional<std::string_view> enumString = enum_name(val);
- result += enumString ? enumString.value() : base::StringPrintf("0x%08x", val);
+ result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
return result;
}
};
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index 09a5f39..cdd7764 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -35,6 +35,11 @@
vendor_available: true,
double_loadable: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
cflags: [
"-Wall",
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..727ea60 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -17,6 +17,7 @@
#include <mutex>
#include <unistd.h>
+#include <android/permission_manager.h>
#include <binder/ActivityManager.h>
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
@@ -98,6 +99,18 @@
return PROCESS_STATE_UNKNOWN;
}
+status_t ActivityManager::checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->checkPermission(permission, pid, uid, outResult);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d363ee9..26b997e 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -31,6 +31,11 @@
"libutils_headers",
],
min_sdk_version: "29",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
// These interfaces are android-specific implementation unrelated to binder
@@ -107,7 +112,9 @@
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
+ "Utils.cpp",
":libbinder_aidl",
+ ":activity_manager_procstate_aidl",
],
target: {
@@ -122,6 +129,9 @@
vendor: {
exclude_srcs: libbinder_device_interface_sources,
},
+ darwin: {
+ enabled: false,
+ },
},
aidl: {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 6ca3b16..f2d223d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -173,6 +173,10 @@
{
data.setDataPosition(0);
+ if (reply != nullptr && (flags & FLAG_CLEAR_BUF)) {
+ reply->markSensitive();
+ }
+
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..e9f5aae 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -17,9 +17,11 @@
#include <unistd.h>
#include <fcntl.h>
+#include <android/permission_manager.h>
#include <binder/ActivityManager.h>
#include <binder/IActivityManager.h>
#include <binder/Parcel.h>
+#include <utils/Errors.h>
namespace android {
@@ -104,6 +106,23 @@
}
return reply.readInt32();
}
+
+ virtual status_t checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeString16(permission);
+ data.writeInt32(pid);
+ data.writeInt32(uid);
+ status_t err = remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ *outResult = reply.readInt32();
+ return NO_ERROR;
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 05fcc2b..a3a2f87 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1244,7 +1244,9 @@
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
if (error < NO_ERROR) reply.setError(error);
- sendReply(reply, 0);
+
+ constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
+ sendReply(reply, (tr.flags & kForwardReplyFlags));
} else {
if (error != OK || reply.dataSize() != 0) {
alog << "oneway function results will be dropped but finished with status "
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 325e204..2e15e50 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -29,16 +29,12 @@
using AidlServiceManager = android::os::IServiceManager;
-class ClientCounterCallback : public ::android::os::BnClientCallback {
+class ClientCounterCallbackImpl : public ::android::os::BnClientCallback {
public:
- ClientCounterCallback() : mNumConnectedServices(0), mForcePersist(false) {}
+ ClientCounterCallbackImpl() : mNumConnectedServices(0), mForcePersist(false) {}
bool registerService(const sp<IBinder>& service, const std::string& name,
bool allowIsolated, int dumpFlags);
-
- /**
- * Set a flag to prevent services from automatically shutting down
- */
void forcePersist(bool persist);
protected:
@@ -75,7 +71,23 @@
bool mForcePersist;
};
-bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+class ClientCounterCallback {
+public:
+ ClientCounterCallback();
+
+ bool registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags);
+
+ /**
+ * Set a flag to prevent services from automatically shutting down
+ */
+ void forcePersist(bool persist);
+
+private:
+ sp<ClientCounterCallbackImpl> mImpl;
+};
+
+bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name,
bool allowIsolated, int dumpFlags) {
auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
@@ -89,7 +101,7 @@
}
if (!reRegister) {
- if (!manager->registerClientCallback(name, service, this).isOk()) {
+ if(!manager->registerClientCallback(name, service, this).isOk()) {
ALOGE("Failed to add client callback for service %s", name.c_str());
return false;
}
@@ -105,7 +117,7 @@
return true;
}
-std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) {
+std::map<std::string, ClientCounterCallbackImpl::Service>::iterator ClientCounterCallbackImpl::assertRegisteredService(const sp<IBinder>& service) {
LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service");
for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) {
auto const& [name, registered] = *it;
@@ -117,7 +129,7 @@
__builtin_unreachable();
}
-void ClientCounterCallback::forcePersist(bool persist) {
+void ClientCounterCallbackImpl::forcePersist(bool persist) {
mForcePersist = persist;
if(!mForcePersist) {
// Attempt a shutdown in case the number of clients hit 0 while the flag was on
@@ -129,7 +141,7 @@
* onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
* invocations could occur on different threads however.
*/
-Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) {
auto & [name, registered] = *assertRegisteredService(service);
if (registered.clients == clients) {
LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
@@ -154,7 +166,7 @@
return Status::ok();
}
-void ClientCounterCallback::tryShutdown() {
+void ClientCounterCallbackImpl::tryShutdown() {
if(mNumConnectedServices > 0) {
// Should only shut down if there are no clients
return;
@@ -175,7 +187,6 @@
bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
-
if (!success) {
ALOGI("Failed to unregister service %s", entry.first.c_str());
break;
@@ -200,6 +211,19 @@
}
}
+ClientCounterCallback::ClientCounterCallback() {
+ mImpl = sp<ClientCounterCallbackImpl>::make();
+}
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ return mImpl->registerService(service, name, allowIsolated, dumpFlags);
+}
+
+void ClientCounterCallback::forcePersist(bool persist) {
+ mImpl->forcePersist(persist);
+}
+
} // namespace internal
LazyServiceRegistrar::LazyServiceRegistrar() {
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index ddd9f9b..041c364 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -49,6 +49,7 @@
#include <private/binder/binder_module.h>
#include "Static.h"
+#include "Utils.h"
#define LOG_REFS(...)
//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -502,6 +503,11 @@
return mHasFds;
}
+void Parcel::markSensitive() const
+{
+ mDeallocZero = true;
+}
+
void Parcel::updateWorkSourceRequestHeaderPosition() const {
// Only update the request headers once. We only want to point
// to the first headers read/written.
@@ -1068,6 +1074,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char));
@@ -1108,6 +1115,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t);
@@ -2625,6 +2633,9 @@
LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
gParcelGlobalAllocSize -= mDataCapacity;
gParcelGlobalAllocCount--;
+ if (mDeallocZero) {
+ zeroMemory(mData, mDataSize);
+ }
free(mData);
}
if (mObjects) free(mObjects);
@@ -2647,6 +2658,21 @@
: continueWrite(std::max(newSize, (size_t) 128));
}
+static uint8_t* reallocZeroFree(uint8_t* data, size_t oldCapacity, size_t newCapacity, bool zero) {
+ if (!zero) {
+ return (uint8_t*)realloc(data, newCapacity);
+ }
+ uint8_t* newData = (uint8_t*)malloc(newCapacity);
+ if (!newData) {
+ return nullptr;
+ }
+
+ memcpy(newData, data, std::min(oldCapacity, newCapacity));
+ zeroMemory(data, oldCapacity);
+ free(data);
+ return newData;
+}
+
status_t Parcel::restartWrite(size_t desired)
{
if (desired > INT32_MAX) {
@@ -2660,7 +2686,7 @@
return continueWrite(desired);
}
- uint8_t* data = (uint8_t*)realloc(mData, desired);
+ uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (!data && desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
@@ -2811,7 +2837,7 @@
// We own the data, so we can just do a realloc().
if (desired > mDataCapacity) {
- uint8_t* data = (uint8_t*)realloc(mData, desired);
+ uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (data) {
LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
desired);
@@ -2879,6 +2905,7 @@
mHasFds = false;
mFdsKnown = true;
mAllowFds = true;
+ mDeallocZero = false;
mOwner = nullptr;
mOpenAshmemSize = 0;
mWorkSourceRequestHeaderPosition = 0;
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c232283..61a611d 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,6 +16,13 @@
"name": "binderTextOutputTest"
},
{
+ "name": "binderParcelTest"
+ },
+ {
+ "name": "binderParcelTest",
+ "host": true
+ },
+ {
"name": "binderLibTest"
},
{
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
new file mode 100644
index 0000000..90a4502
--- /dev/null
+++ b/libs/binder/Utils.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 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 "Utils.h"
+
+#include <string.h>
+
+namespace android {
+
+void zeroMemory(uint8_t* data, size_t size) {
+ memset(data, 0, size);
+}
+
+} // namespace android
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
new file mode 100644
index 0000000..f94b158
--- /dev/null
+++ b/libs/binder/Utils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 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 <cstdint>
+#include <stddef.h>
+
+namespace android {
+
+// avoid optimizations
+void zeroMemory(uint8_t* data, size_t size);
+
+} // namespace android
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..e3b7cb9 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -20,12 +20,16 @@
#ifndef __ANDROID_VNDK__
#include <binder/IActivityManager.h>
+#include <android/app/ProcessStateEnum.h>
#include <utils/threads.h>
// ---------------------------------------------------------------------------
namespace android {
+#define DECLARE_PROCESS_STATE(name) \
+ PROCESS_STATE_##name = (int32_t) app::ProcessStateEnum::name
+
class ActivityManager
{
public:
@@ -41,30 +45,31 @@
UID_OBSERVER_ACTIVE = 1<<3
};
+ // PROCESS_STATE_* must come from frameworks/base/core/java/android/app/ProcessStateEnum.aidl.
+ // This is to make sure that Java side uses the same values as native.
enum {
- PROCESS_STATE_UNKNOWN = -1,
- PROCESS_STATE_PERSISTENT = 0,
- PROCESS_STATE_PERSISTENT_UI = 1,
- PROCESS_STATE_TOP = 2,
- PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
- PROCESS_STATE_BOUND_TOP = 4,
- PROCESS_STATE_FOREGROUND_SERVICE = 5,
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
- PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
- PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
- PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
- PROCESS_STATE_BACKUP = 10,
- PROCESS_STATE_SERVICE = 11,
- PROCESS_STATE_RECEIVER = 12,
- PROCESS_STATE_TOP_SLEEPING = 13,
- PROCESS_STATE_HEAVY_WEIGHT = 14,
- PROCESS_STATE_HOME = 15,
- PROCESS_STATE_LAST_ACTIVITY = 16,
- PROCESS_STATE_CACHED_ACTIVITY = 17,
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
- PROCESS_STATE_CACHED_RECENT = 19,
- PROCESS_STATE_CACHED_EMPTY = 20,
- PROCESS_STATE_NONEXISTENT = 21,
+ DECLARE_PROCESS_STATE(UNKNOWN),
+ DECLARE_PROCESS_STATE(PERSISTENT),
+ DECLARE_PROCESS_STATE(PERSISTENT_UI),
+ DECLARE_PROCESS_STATE(TOP),
+ DECLARE_PROCESS_STATE(BOUND_TOP),
+ DECLARE_PROCESS_STATE(FOREGROUND_SERVICE),
+ DECLARE_PROCESS_STATE(BOUND_FOREGROUND_SERVICE),
+ DECLARE_PROCESS_STATE(IMPORTANT_FOREGROUND),
+ DECLARE_PROCESS_STATE(IMPORTANT_BACKGROUND),
+ DECLARE_PROCESS_STATE(TRANSIENT_BACKGROUND),
+ DECLARE_PROCESS_STATE(BACKUP),
+ DECLARE_PROCESS_STATE(SERVICE),
+ DECLARE_PROCESS_STATE(RECEIVER),
+ DECLARE_PROCESS_STATE(TOP_SLEEPING),
+ DECLARE_PROCESS_STATE(HEAVY_WEIGHT),
+ DECLARE_PROCESS_STATE(HOME),
+ DECLARE_PROCESS_STATE(LAST_ACTIVITY),
+ DECLARE_PROCESS_STATE(CACHED_ACTIVITY),
+ DECLARE_PROCESS_STATE(CACHED_ACTIVITY_CLIENT),
+ DECLARE_PROCESS_STATE(CACHED_RECENT),
+ DECLARE_PROCESS_STATE(CACHED_EMPTY),
+ DECLARE_PROCESS_STATE(NONEXISTENT),
};
ActivityManager();
@@ -77,9 +82,9 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
+ status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
-
- status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+ status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
private:
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..4573347 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,18 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual status_t checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ CHECK_PERMISSION_TRANSACTION,
};
};
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index eea0e89..7b37e8d 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -64,6 +64,10 @@
// Corresponds to TF_ONE_WAY -- an asynchronous call.
FLAG_ONEWAY = 0x00000001,
+ // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
+ // is made
+ FLAG_CLEAR_BUF = 0x00000020,
+
// Private userspace flag for transaction which is being requested from
// a vendor context.
FLAG_PRIVATE_VENDOR = 0x10000000,
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index fbfd6c5..84bfe38 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -84,6 +84,13 @@
bool hasFileDescriptors() const;
+ // Zeros data when reallocating. Other mitigations may be added
+ // in the future.
+ //
+ // WARNING: some read methods may make additional copies of data.
+ // In order to verify this, heap dumps should be used.
+ void markSensitive() const;
+
// Writes the RPC header.
status_t writeInterfaceToken(const String16& interface);
status_t writeInterfaceToken(const char16_t* str, size_t len);
@@ -600,6 +607,10 @@
mutable bool mHasFds;
bool mAllowFds;
+ // if this parcelable is involved in a secure transaction, force the
+ // data to be overridden with zero when deallocated
+ mutable bool mDeallocZero;
+
release_func mOwner;
void* mOwnerCookie;
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index 7be8f7b..0fe7f5b 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -88,7 +88,9 @@
};
#endif //BINDER_GET_FROZEN_INFO
-
+enum transaction_flags_ext {
+ TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */
+};
#ifdef __cplusplus
} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index e4d86ae..cecc759 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -26,6 +26,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
@@ -84,6 +87,9 @@
linux: {
version_script: "libbinder_ndk.map.txt",
},
+ darwin: {
+ enabled: false,
+ },
},
stubs: {
symbol_file: "libbinder_ndk.map.txt",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 4610ba9..b927f6f 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include "ibinder_internal.h"
#include <android/binder_stability.h>
@@ -611,7 +611,7 @@
return STATUS_UNKNOWN_TRANSACTION;
}
- constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY;
+ constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF;
if ((flags & ~kAllFlags) != 0) {
LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags;
return STATUS_BAD_VALUE;
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 18877af..8d60226 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -313,7 +313,8 @@
/**
* Takes ownership of a.
*/
- explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {}
+ ScopedFileDescriptor() : ScopedFileDescriptor(-1) {}
+ explicit ScopedFileDescriptor(int a) : ScopedAResource(a) {}
~ScopedFileDescriptor() {}
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 33763d5..ce3d1db 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -43,7 +43,6 @@
#if __ANDROID_API__ >= 29
-// Also see TF_* in kernel's binder.h
typedef uint32_t binder_flags_t;
enum {
/**
diff --git a/libs/binder/ndk/include_platform/android/binder_context.h b/libs/binder/ndk/include_platform/android/binder_context.h
deleted file mode 100644
index a99d555..0000000
--- a/libs/binder/ndk/include_platform/android/binder_context.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 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 <android/binder_ibinder.h>
-
-__BEGIN_DECLS
-
-/**
- * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
- * must be called on a local binder server before it is sent out to any othe
- * process. If this is a remote binder, it will abort. If the kernel doesn't
- * support this feature, you'll always get null from AIBinder_getCallingSid.
- *
- * \param binder local server binder to request security contexts on
- */
-__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
- __INTRODUCED_IN(31);
-
-/**
- * Returns the selinux context of the callee.
- *
- * In order for this to work, the following conditions must be met:
- * - The kernel must be new enough to support this feature.
- * - The server must have called AIBinder_setRequestingSid.
- * - The callee must be a remote process.
- *
- * \return security context or null if unavailable. The lifetime of this context
- * is the lifetime of the transaction.
- */
-__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-
-__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index 2af65cf..e315c79 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -16,39 +16,43 @@
#pragma once
-// binder_context.h used to be part of this header and is included for backwards
-// compatibility.
-#include <android/binder_context.h>
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
-
#include <android/binder_ibinder.h>
-#include <binder/IBinder.h>
+
+__BEGIN_DECLS
+
+// platform values for binder_flags_t
+enum {
+ /**
+ * The transaction and reply will be cleared by the kernel in read-only
+ * binder buffers storing transactions.
+ *
+ * Introduced in API level 31.
+ */
+ FLAG_CLEAR_BUF = 0x20,
+};
/**
- * Get libbinder version of binder from AIBinder.
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
- *
- * \param binder binder with ownership retained by the client
- * \return platform binder object
+ * \param binder local server binder to request security contexts on
*/
-android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+ __INTRODUCED_IN(31);
/**
- * Get libbinder_ndk version of binder from platform binder.
+ * Returns the selinux context of the callee.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
*
- * \param binder platform binder which may be from anywhere (doesn't have to be
- * created with libbinder_ndK)
- * \return binder with one reference count of ownership given to the client. See
- * AIBinder_decStrong
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
*/
-AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-#endif
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
new file mode 100644
index 0000000..f0c00e8
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
+#include <android/binder_ibinder.h>
+#include <binder/IBinder.h>
+
+/**
+ * Get libbinder version of binder from AIBinder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder binder with ownership retained by the client
+ * \return platform binder object
+ */
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+
+/**
+ * Get libbinder_ndk version of binder from platform binder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder platform binder which may be from anywhere (doesn't have to be
+ * created with libbinder_ndK)
+ * \return binder with one reference count of ownership given to the client. See
+ * AIBinder_decStrong
+ */
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+
+#endif
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index 114a781..d54c1a1 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -33,4 +33,15 @@
*/
bool AParcel_getAllowFds(const AParcel*);
+/**
+ * Data written to the parcel will be zero'd before being deleted or realloced.
+ *
+ * The main use of this is marking a parcel that will be used in a transaction
+ * with FLAG_CLEAR_BUF. When FLAG_CLEAR_BUF is used, the reply parcel will
+ * automatically be marked as sensitive when it is created.
+ *
+ * \param parcel The parcel to clear associated data from.
+ */
+void AParcel_markSensitive(const AParcel* parcel);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 947cc98..6962f86 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -121,15 +121,16 @@
AServiceManager_registerLazyService; # llndk
AServiceManager_waitForService; # apex llndk
- AParcel_reset;
- AParcel_getDataSize;
AParcel_appendFrom;
AParcel_create;
+ AParcel_getDataSize;
+ AParcel_reset;
};
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
+ AParcel_markSensitive;
extern "C++" {
AIBinder_fromPlatformBinder*;
AIBinder_toPlatformBinder*;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 2f95318..3e3eda1 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -226,6 +226,10 @@
return parcel->get()->dataPosition();
}
+void AParcel_markSensitive(const AParcel* parcel) {
+ return parcel->get()->markSensitive();
+}
+
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) {
sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr;
return parcel->get()->writeStrongBinder(writeBinder);
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 160b9f2..f84d9d3 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -18,9 +18,9 @@
#include <aidl/BnBinderNdkUnitTest.h>
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
-#include <android/binder_context.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp
index c5b3d80..3e6fe99 100644
--- a/libs/binder/parcel_fuzzer/Android.bp
+++ b/libs/binder/parcel_fuzzer/Android.bp
@@ -52,6 +52,11 @@
cc_library_static {
name: "libbinder_random_parcel",
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
srcs: [
"random_fd.cpp",
"random_parcel.cpp",
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index dc8270e..fd5f2f5 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -10,6 +10,11 @@
"libbinder_ndk_sys",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_library {
@@ -23,6 +28,11 @@
"libbinder_ndk",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_bindgen {
@@ -64,6 +74,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 6d0a369..037ee95 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -33,8 +33,7 @@
/// Additional operation flags.
///
-/// Can be either 0 for a normal RPC, or [`IBinder::FLAG_ONEWAY`] for a
-/// one-way RPC.
+/// `IBinder::FLAG_*` values.
pub type TransactionFlags = u32;
/// Super-trait for Binder interfaces.
@@ -91,6 +90,8 @@
/// Corresponds to TF_ONE_WAY -- an asynchronous call.
const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+ /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+ const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
/// Is this object still alive?
fn is_binder_alive(&self) -> bool;
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 2c1e5a4..6c34824 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -100,6 +100,14 @@
// Data serialization methods
impl Parcel {
+ /// Data written to parcelable is zero'd before being deleted or reallocated.
+ pub fn mark_sensitive(&mut self) {
+ unsafe {
+ // Safety: guaranteed to have a parcel object, and this method never fails
+ sys::AParcel_markSensitive(self.as_native())
+ }
+ }
+
/// Write a type that implements [`Serialize`] to the `Parcel`.
pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
parcelable.serialize(self)
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 303f4a5..ef142b5 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <android/binder_parcel.h>
+#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
#include <android/binder_shell.h>
#include <android/binder_status.h>
@@ -78,6 +79,7 @@
enum {
FLAG_ONEWAY = FLAG_ONEWAY,
+ FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
};
} // namespace consts
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a03835b..87f1d45 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -60,6 +60,23 @@
require_root: true,
}
+// unit test only, which can run on host and doesn't use /dev/binder
+cc_test {
+ name: "binderParcelTest",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: ["binderParcelTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
+
cc_test {
name: "binderLibTest",
defaults: ["binder_test_defaults"],
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 98f0868..ad4729d 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -402,6 +402,14 @@
EXPECT_EQ(NO_ERROR, ret);
}
+TEST_F(BinderLibTest, NopTransactionClear) {
+ status_t ret;
+ Parcel data, reply;
+ // make sure it accepts the transaction flag
+ ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
TEST_F(BinderLibTest, Freeze) {
status_t ret;
Parcel data, reply, replypid;
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp
new file mode 100644
index 0000000..1764228
--- /dev/null
+++ b/libs/binder/tests/binderParcelTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 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/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <gtest/gtest.h>
+
+using android::IPCThreadState;
+using android::OK;
+using android::Parcel;
+using android::String16;
+using android::String8;
+using android::status_t;
+
+// Tests a second operation results in a parcel at the same location as it
+// started.
+void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
+ Parcel p;
+ a(&p);
+ size_t end = p.dataPosition();
+ p.setDataPosition(0);
+ b(&p);
+ EXPECT_EQ(end, p.dataPosition());
+}
+
+TEST(Parcel, InverseInterfaceToken) {
+ const String16 token = String16("asdf");
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeInterfaceToken(token);
+ }, [&] (Parcel* p) {
+ EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
+ });
+}
+
+TEST(Parcel, Utf8FromUtf16Read) {
+ const char* token = "asdf";
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeString16(String16(token));
+ }, [&] (Parcel* p) {
+ std::string s;
+ EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
+ EXPECT_EQ(token, s);
+ });
+}
+
+TEST(Parcel, Utf8AsUtf16Write) {
+ std::string token = "asdf";
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeUtf8AsUtf16(token);
+ }, [&] (Parcel* p) {
+ String16 s;
+ EXPECT_EQ(OK, p->readString16(&s));
+ EXPECT_EQ(s, String16(token.c_str()));
+ });
+}
+
+template <typename T>
+using readFunc = status_t (Parcel::*)(T* out) const;
+template <typename T>
+using writeFunc = status_t (Parcel::*)(const T& in);
+template <typename T>
+using copyWriteFunc = status_t (Parcel::*)(T in);
+
+template <typename T, typename WRITE_FUNC>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, WRITE_FUNC w) {
+ for (const T& value : ts) {
+ parcelOpSameLength([&] (Parcel* p) {
+ (*p.*w)(value);
+ }, [&] (Parcel* p) {
+ T outValue;
+ EXPECT_EQ(OK, (*p.*r)(&outValue));
+ EXPECT_EQ(value, outValue);
+ });
+ }
+}
+
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, writeFunc<T> w) {
+ readWriteInverse<T, writeFunc<T>>(std::move(ts), r, w);
+}
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, copyWriteFunc<T> w) {
+ readWriteInverse<T, copyWriteFunc<T>>(std::move(ts), r, w);
+}
+
+#define TEST_READ_WRITE_INVERSE(type, name, ...) \
+ TEST(Parcel, Inverse##name) { \
+ readWriteInverse<type>(__VA_ARGS__, &Parcel::read##name, &Parcel::write##name); \
+ }
+
+TEST_READ_WRITE_INVERSE(int32_t, Int32, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint32_t, Uint32, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(int64_t, Int64, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint64_t, Uint64, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(float, Float, {-1.0f, 0.0f, 3.14f});
+TEST_READ_WRITE_INVERSE(double, Double, {-1.0, 0.0, 3.14});
+TEST_READ_WRITE_INVERSE(bool, Bool, {true, false});
+TEST_READ_WRITE_INVERSE(char16_t, Char, {u'a', u'\0'});
+TEST_READ_WRITE_INVERSE(int8_t, Byte, {-1, 0, 1});
+TEST_READ_WRITE_INVERSE(String8, String8, {String8(), String8("a"), String8("asdf")});
+TEST_READ_WRITE_INVERSE(String16, String16, {String16(), String16("a"), String16("asdf")});
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
index 46379fc..c465bed 100644
--- a/libs/binder/tests/fuzzers/Android.bp
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -26,6 +26,11 @@
"libutils",
"libbase",
],
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
cc_fuzz {
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 88752ee..08c62df 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -19,6 +19,11 @@
double_loadable: true,
vendor_available: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
shared_libs: [
"libbinder",
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 6909637..76518c1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -9,6 +9,11 @@
"libbinder",
"libutils",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_library {
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index e11be57..fb32382 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -5,6 +5,7 @@
address: true,
},
srcs: [
+ "SmallVector_test.cpp",
"StaticVector_test.cpp",
],
cflags: [
diff --git a/libs/ftl/SmallVector_test.cpp b/libs/ftl/SmallVector_test.cpp
new file mode 100644
index 0000000..c41e622
--- /dev/null
+++ b/libs/ftl/SmallVector_test.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2020 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/SmallVector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::SmallVector;
+
+// Keep in sync with example usage in header file.
+TEST(SmallVector, Example) {
+ ftl::SmallVector<char, 3> vector;
+ EXPECT_TRUE(vector.empty());
+ EXPECT_FALSE(vector.dynamic());
+
+ vector = {'a', 'b', 'c'};
+ EXPECT_EQ(vector.size(), 3u);
+ EXPECT_FALSE(vector.dynamic());
+
+ vector.push_back('d');
+ EXPECT_TRUE(vector.dynamic());
+
+ vector.unstable_erase(vector.begin());
+ EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'}));
+
+ vector.pop_back();
+ EXPECT_EQ(vector.back(), 'b');
+ EXPECT_TRUE(vector.dynamic());
+
+ const char array[] = "hi";
+ vector = ftl::SmallVector(array);
+ EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
+ EXPECT_FALSE(vector.dynamic());
+}
+
+TEST(SmallVector, Construct) {
+ {
+ // Default constructor.
+ SmallVector<std::string, 2> vector;
+
+ EXPECT_TRUE(vector.empty());
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Array constructor.
+ const float kFloats[] = {.1f, .2f, .3f};
+ SmallVector vector(kFloats);
+
+ EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Iterator constructor.
+ const char chars[] = "abcdef";
+ std::string string(chars);
+ SmallVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+ EXPECT_STREQ(vector.begin(), chars);
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Variadic constructor with same types.
+ SmallVector vector = {1, 2, 3};
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>);
+ EXPECT_EQ(vector, (SmallVector{1, 2, 3}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Variadic constructor with different types.
+ const auto copy = "quince"s;
+ auto move = "tart"s;
+ SmallVector vector = {copy, std::move(move)};
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>);
+ EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // In-place constructor with same types.
+ SmallVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+ EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // In-place constructor with different types.
+ const auto copy = "red"s;
+ auto move = "velvet"s;
+ std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+ SmallVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+ EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Conversion from StaticVector.
+ ftl::StaticVector doubles = {.1, .2, .3};
+ SmallVector vector = std::move(doubles);
+ EXPECT_TRUE(doubles.empty());
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>);
+ EXPECT_EQ(vector, (SmallVector{.1, .2, .3}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+}
+
+TEST(SmallVector, String) {
+ SmallVector<char, 10> chars;
+ char c = 'a';
+ std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+ chars.push_back('\0');
+
+ EXPECT_TRUE(chars.dynamic());
+ EXPECT_EQ(chars.size(), 11u);
+ EXPECT_STREQ(chars.begin(), "abcdefghij");
+
+ // Constructor takes iterator range.
+ const char kString[] = "123456";
+ SmallVector<char, 10> string(std::begin(kString), std::end(kString));
+
+ EXPECT_FALSE(string.dynamic());
+ EXPECT_STREQ(string.begin(), "123456");
+ EXPECT_EQ(string.size(), 7u);
+
+ // Similar to emplace, but replaces rather than inserts.
+ string.replace(string.begin() + 5, '\0');
+ EXPECT_STREQ(string.begin(), "12345");
+
+ swap(chars, string);
+
+ EXPECT_STREQ(chars.begin(), "12345");
+ EXPECT_STREQ(string.begin(), "abcdefghij");
+
+ EXPECT_FALSE(chars.dynamic());
+ EXPECT_TRUE(string.dynamic());
+}
+
+TEST(SmallVector, CopyableElement) {
+ struct Pair {
+ // Needed because std::vector emplace does not use uniform initialization.
+ Pair(int a, int b) : a(a), b(b) {}
+
+ const int a, b;
+ bool operator==(Pair p) const { return p.a == a && p.b == b; }
+ };
+
+ SmallVector<Pair, 5> pairs;
+
+ EXPECT_TRUE(pairs.empty());
+ EXPECT_EQ(pairs.max_size(), 5u);
+
+ for (size_t i = 0; i < pairs.max_size(); ++i) {
+ EXPECT_EQ(pairs.size(), i);
+
+ const int a = static_cast<int>(i) * 2;
+ EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1));
+ }
+
+ EXPECT_EQ(pairs.size(), 5u);
+ EXPECT_FALSE(pairs.dynamic());
+
+ // The vector is promoted when full.
+ EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11));
+ EXPECT_TRUE(pairs.dynamic());
+
+ EXPECT_EQ(pairs,
+ (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9},
+ Pair{10, 11}}));
+
+ // Constructor takes at most N elements.
+ SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0};
+ EXPECT_FALSE(sums.dynamic());
+
+ // Random-access iterators comply with standard.
+ std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+ EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21}));
+
+ sums.pop_back();
+ std::reverse(sums.begin(), sums.end());
+
+ EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1}));
+}
+
+TEST(SmallVector, MovableElement) {
+ // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
+ // element type would be deduced from the first element, i.e. const char*.
+ SmallVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+ strings.pop_back();
+
+ EXPECT_EQ(strings.max_size(), 7u);
+ EXPECT_EQ(strings.size(), 6u);
+
+ // Erase "cake" and append a substring copy.
+ {
+ const auto it = std::find_if(strings.begin(), strings.end(),
+ [](const auto& s) { return !s.empty(); });
+ ASSERT_FALSE(it == strings.end());
+ EXPECT_EQ(*it, "cake");
+
+ strings.unstable_erase(it);
+ EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
+ }
+
+ strings[1] = "quince"s;
+
+ // Replace last empty string with "tart".
+ {
+ const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+ ASSERT_FALSE(rit == strings.rend());
+
+ std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+ strings.replace(rit.base() - 1, list);
+ }
+
+ strings.front().assign("pie");
+
+ EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+
+ strings.push_back("nougat");
+ strings.push_back("oreo");
+ EXPECT_TRUE(strings.dynamic());
+
+ std::rotate(strings.begin(), strings.end() - 2, strings.end());
+
+ EXPECT_EQ(strings,
+ (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s,
+ "cake"s}));
+}
+
+TEST(SmallVector, Replace) {
+ // Replacing does not require a copy/move assignment operator.
+ struct Word {
+ explicit Word(std::string str) : str(std::move(str)) {}
+ const std::string str;
+
+ bool operator==(const Word& other) const { return other.str == str; }
+ };
+
+ SmallVector words(std::in_place_type<Word>, "colored", "velour");
+
+ // The replaced element can be referenced by the replacement.
+ {
+ const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet");
+ EXPECT_EQ(word, Word("velvet"));
+ }
+
+ // The vector is not promoted if replacing while full.
+ EXPECT_FALSE(words.dynamic());
+
+ words.emplace_back("cake");
+ EXPECT_TRUE(words.dynamic());
+
+ {
+ const Word& word = words.replace(words.begin(), words.front().str.substr(4));
+ EXPECT_EQ(word, Word("red"));
+ }
+
+ EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")}));
+}
+
+TEST(SmallVector, ReverseAppend) {
+ SmallVector strings = {"red"s, "velvet"s, "cake"s};
+ EXPECT_FALSE(strings.dynamic());
+
+ auto rit = strings.rbegin();
+ while (rit != strings.rend()) {
+ // Iterator and reference are invalidated on insertion.
+ const auto i = std::distance(strings.begin(), rit.base());
+ std::string s = *rit;
+
+ strings.push_back(std::move(s));
+ rit = std::make_reverse_iterator(strings.begin() + i) + 1;
+ }
+
+ EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s}));
+ EXPECT_TRUE(strings.dynamic());
+}
+
+TEST(SmallVector, Sort) {
+ SmallVector strings(std::in_place_type<std::string>, "pie", "quince", "tart", "red", "velvet");
+ strings.push_back("cake"s);
+
+ auto sorted = std::move(strings);
+ EXPECT_TRUE(strings.empty());
+
+ EXPECT_TRUE(sorted.dynamic());
+ EXPECT_TRUE(strings.dynamic());
+
+ std::sort(sorted.begin(), sorted.end());
+ EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+ // Constructor takes array reference.
+ {
+ const char* kStrings[] = {"cake", "lie"};
+ strings = SmallVector(kStrings);
+ EXPECT_FALSE(strings.dynamic());
+ }
+
+ EXPECT_GT(sorted, strings);
+ swap(sorted, strings);
+ EXPECT_LT(sorted, strings);
+
+ EXPECT_FALSE(sorted.dynamic());
+ EXPECT_TRUE(strings.dynamic());
+
+ // Append remaining elements, such that "pie" is the only difference.
+ for (const char* str : {"quince", "red", "tart", "velvet"}) {
+ sorted.emplace_back(str);
+ }
+ EXPECT_TRUE(sorted.dynamic());
+
+ EXPECT_NE(sorted, strings);
+
+ // Replace second element with "pie".
+ const auto it = sorted.begin() + 1;
+ EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+ EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+ DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+ DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+ DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+ ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+ struct {
+ int& live;
+ int& dead;
+ } counts;
+
+ bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+ std::swap(lhs.alive, rhs.alive);
+}
+
+} // namespace
+
+TEST(SmallVector, Destroy) {
+ int live = 0;
+ int dead = 0;
+
+ { SmallVector<DestroyCounts, 3> counts; }
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(0, dead);
+
+ {
+ SmallVector<DestroyCounts, 3> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ EXPECT_FALSE(counts.dynamic());
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ {
+ SmallVector<DestroyCounts, 3> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ EXPECT_TRUE(counts.dynamic());
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(3, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto copy = counts;
+ EXPECT_TRUE(copy.dynamic());
+ }
+ EXPECT_EQ(6, live);
+ EXPECT_EQ(2, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto move = std::move(counts);
+ EXPECT_TRUE(move.dynamic());
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(2, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts1;
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+
+ EXPECT_TRUE(counts1.dynamic());
+ EXPECT_EQ(2, dead);
+ dead = 0;
+
+ SmallVector<DestroyCounts, 2> counts2;
+ counts2.emplace_back(live, dead);
+
+ EXPECT_FALSE(counts2.dynamic());
+
+ swap(counts1, counts2);
+
+ EXPECT_FALSE(counts1.dynamic());
+ EXPECT_TRUE(counts2.dynamic());
+
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(1, dead);
+
+ dead = 0;
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(0, dead);
+}
+
+} // namespace android::test
diff --git a/libs/ftl/StaticVector_test.cpp b/libs/ftl/StaticVector_test.cpp
index 06e4659..dd5ce35 100644
--- a/libs/ftl/StaticVector_test.cpp
+++ b/libs/ftl/StaticVector_test.cpp
@@ -106,6 +106,33 @@
static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
}
+ {
+ struct String {
+ explicit String(const char* str) : str(str) {}
+ explicit String(const char** ptr) : str(*ptr) {}
+ const char* str;
+ };
+
+ const char* kStrings[] = {"a", "b", "c", "d"};
+
+ {
+ // Two iterator-like elements.
+ StaticVector<String, 3> vector(kStrings, kStrings + 3);
+ ASSERT_EQ(vector.size(), 2u);
+
+ EXPECT_STREQ(vector[0].str, "a");
+ EXPECT_STREQ(vector[1].str, "d");
+ }
+ {
+ // Disambiguating iterator constructor.
+ StaticVector<String, 3> vector(ftl::IteratorRange, kStrings, kStrings + 3);
+ ASSERT_EQ(vector.size(), 3u);
+
+ EXPECT_STREQ(vector[0].str, "a");
+ EXPECT_STREQ(vector[1].str, "b");
+ EXPECT_STREQ(vector[2].str, "c");
+ }
+ }
}
TEST(StaticVector, String) {
@@ -124,8 +151,7 @@
EXPECT_EQ(string.size(), 7u);
// Similar to emplace, but replaces rather than inserts.
- const auto it = string.replace(string.begin() + 5, '\0');
- EXPECT_NE(it, string.end());
+ string.replace(string.begin() + 5, '\0');
EXPECT_STREQ(string.begin(), "12345");
swap(chars, string);
@@ -209,8 +235,7 @@
ASSERT_FALSE(rit == strings.rend());
std::initializer_list<char> list = {'t', 'a', 'r', 't'};
- const auto it = strings.replace(rit.base() - 1, list);
- EXPECT_NE(it, strings.end());
+ strings.replace(rit.base() - 1, list);
}
strings.front().assign("pie");
@@ -218,6 +243,21 @@
EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
}
+TEST(StaticVector, Replace) {
+ // Replacing does not require a copy/move assignment operator.
+ struct Word {
+ explicit Word(std::string str) : str(std::move(str)) {}
+ const std::string str;
+ };
+
+ StaticVector words(std::in_place_type<Word>, "red", "velour", "cake");
+
+ // The replaced element can be referenced by the replacement.
+ const auto it = words.begin() + 1;
+ const Word& word = words.replace(it, it->str.substr(0, 3) + "vet");
+ EXPECT_EQ(word.str, "velvet");
+}
+
TEST(StaticVector, ReverseTruncate) {
StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake");
EXPECT_FALSE(strings.full());
@@ -251,12 +291,16 @@
EXPECT_LT(sorted, strings);
// Append remaining elements, such that "pie" is the only difference.
- sorted.replace(sorted.end(), "quince");
- for (const char* str : {"red", "tart", "velvet"}) sorted.emplace_back(str);
+ for (const char* str : {"quince", "red", "tart", "velvet"}) {
+ sorted.emplace_back(str);
+ }
EXPECT_NE(sorted, strings);
- sorted.replace(sorted.begin() + 1, "pie");
+ // Replace second element with "pie".
+ const auto it = sorted.begin() + 1;
+ EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
EXPECT_EQ(sorted, strings);
}
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cc31cd5..243d7f1 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -21,6 +21,11 @@
"-Wno-enum-compare",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
vendor_available: true,
vndk: {
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
index 8444883..8933dc3 100644
--- a/libs/gralloc/types/fuzzer/Android.bp
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -2,6 +2,11 @@
name: "libgralloctypes_fuzzer",
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
fuzz_config: {
cc: ["marissaw@google.com"],
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index ff64d65..678613b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -159,7 +159,7 @@
if (context == nullptr) {
return;
}
- BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context);
+ sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
bq->transactionCallback(latchTime, presentFence, stats);
}
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index b3eb994..30c9b37 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -122,6 +122,8 @@
return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
case view::LayerMetadataKey::METADATA_TASK_ID:
return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+ case view::LayerMetadataKey::METADATA_OWNER_PID:
+ return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
default:
return StringPrintf("%d%s%dbytes", key, separator,
static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 4a92f53..9e9e191 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -52,6 +52,7 @@
eFXSurfaceMask = 0x000F0000,
};
+ // TODO(b/172002646): Clean up the Surface Creation Arguments
/*
* Requires ACCESS_SURFACE_FLINGER permission
*/
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index d58e019..ac48aef 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -27,6 +27,8 @@
METADATA_WINDOW_TYPE = 2,
METADATA_TASK_ID = 3,
METADATA_MOUSE_CURSOR = 4,
+ METADATA_ACCESSIBILITY_ID = 5,
+ METADATA_OWNER_PID = 6,
};
struct LayerMetadata : public Parcelable {
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9299721..f3559fa 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -123,6 +123,7 @@
.apply();
mCaptureArgs.displayToken = mDisplayToken;
+ mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
}
void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
@@ -181,6 +182,7 @@
for (uint32_t row = 0; row < height; row++) {
for (uint32_t col = 0; col < width; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+ ASSERT_NE(nullptr, pixel);
bool inRegion;
if (!outsideRegion) {
inRegion = row >= region.top + border && row < region.bottom - border &&
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 483f171..c39b0b5 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -153,6 +153,24 @@
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
+ void expectTapWithFlag(int x, int y, int32_t flags) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+ EXPECT_EQ(x, mev->getX(0));
+ EXPECT_EQ(y, mev->getY(0));
+ EXPECT_EQ(flags, mev->getFlags() & flags);
+
+ ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ EXPECT_EQ(flags, mev->getFlags() & flags);
+ }
+
~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
@@ -602,4 +620,68 @@
surface->expectTap(5, 10);
}
+TEST_F(InputSurfacesTest, touch_flag_obscured) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to fully cover touchable window. Window behind gets touch, but
+ // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(100, 100);
+
+ injectTap(190, 199);
+ surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to cover touchable window, but parent is cropped to not cover area
+ // that will be tapped. Window behind gets touch, but with flag
+ // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(0, 0);
+ parentSurface->showAt(100, 100);
+
+ nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ });
+
+ injectTap(190, 199);
+ surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to cover touchable window, but parent is cropped to avoid covering
+ // the touchable window. Window behind gets touch with no obscured flags.
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(0, 0);
+ parentSurface->showAt(50, 50);
+
+ nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ });
+
+ injectTap(101, 110);
+ surface->expectTap(1, 10);
+}
+
} // namespace android::test
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 3e49de6..1bfe462 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -75,13 +75,9 @@
}
String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
- size_t len;
- const char16_t* str = parcel->readString16Inplace(&len);
- if (str != nullptr) {
- return String16(str, len);
- } else {
- return String16();
- }
+ std::optional<String16> str;
+ parcel->readString16(&str);
+ return str.value_or(String16());
}
} // namespace view
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index ad7db75..0ea3889 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -28,7 +28,9 @@
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
+#ifdef __linux__
#include <binder/Parcel.h>
+#endif
#ifdef __ANDROID__
#include <sys/random.h>
#endif
@@ -254,6 +256,7 @@
setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
}
+#ifdef __linux__
status_t PointerCoords::readFromParcel(Parcel* parcel) {
bits = parcel->readInt64();
@@ -277,6 +280,7 @@
}
return OK;
}
+#endif
void PointerCoords::tooManyAxes(int axis) {
ALOGW("Could not set value for axis %d because the PointerCoords structure is full and "
@@ -538,6 +542,7 @@
}
}
+#ifdef __linux__
static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
float dsdx, dtdx, tx, dtdy, dsdy, ty;
status_t status = parcel.readFloat(&dsdx);
@@ -674,6 +679,7 @@
}
return OK;
}
+#endif
bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
if (source & AINPUT_SOURCE_CLASS_POINTER) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 2623ecd..f5432ad 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -19,7 +19,9 @@
#include <stdlib.h>
#include <string.h>
+#ifdef __linux__
#include <binder/Parcel.h>
+#endif
#include <android/keycodes.h>
#include <attestation/HmacKeyManager.h>
#include <input/InputEventLabels.h>
@@ -83,9 +85,7 @@
// --- KeyCharacterMap ---
-KeyCharacterMap::KeyCharacterMap() :
- mType(KEYBOARD_TYPE_UNKNOWN) {
-}
+KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {}
KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
: mType(other.mType),
@@ -184,7 +184,7 @@
mLoadFileName = overlay.mLoadFileName;
}
-int32_t KeyCharacterMap::getKeyboardType() const {
+KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const {
return mType;
}
@@ -587,13 +587,14 @@
}
}
+#ifdef __linux__
std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
if (parcel == nullptr) {
ALOGE("%s: Null parcel", __func__);
return nullptr;
}
std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
- map->mType = parcel->readInt32();
+ map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
size_t numKeys = parcel->readInt32();
if (parcel->errorCheck()) {
return nullptr;
@@ -651,7 +652,7 @@
ALOGE("%s: Null parcel", __func__);
return;
}
- parcel->writeInt32(mType);
+ parcel->writeInt32(static_cast<int32_t>(mType));
size_t numKeys = mKeys.size();
parcel->writeInt32(numKeys);
@@ -672,7 +673,7 @@
parcel->writeInt32(0);
}
}
-
+#endif // __linux__
// --- KeyCharacterMap::Key ---
@@ -776,20 +777,20 @@
return BAD_VALUE;
}
- if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
+ if (mMap->mType == KeyboardType::UNKNOWN) {
ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- if (mFormat == FORMAT_BASE) {
- if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
+ if (mFormat == Format::BASE) {
+ if (mMap->mType == KeyboardType::OVERLAY) {
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- } else if (mFormat == FORMAT_OVERLAY) {
- if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
+ } else if (mFormat == Format::OVERLAY) {
+ if (mMap->mType != KeyboardType::OVERLAY) {
ALOGE("%s: Overlay keyboard layout missing required keyboard "
"'type OVERLAY' declaration.",
mTokenizer->getLocation().string());
@@ -801,7 +802,7 @@
}
status_t KeyCharacterMap::Parser::parseType() {
- if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
+ if (mMap->mType != KeyboardType::UNKNOWN) {
ALOGE("%s: Duplicate keyboard 'type' declaration.",
mTokenizer->getLocation().string());
return BAD_VALUE;
@@ -810,20 +811,20 @@
KeyboardType type;
String8 typeToken = mTokenizer->nextToken(WHITESPACE);
if (typeToken == "NUMERIC") {
- type = KEYBOARD_TYPE_NUMERIC;
+ type = KeyboardType::NUMERIC;
} else if (typeToken == "PREDICTIVE") {
- type = KEYBOARD_TYPE_PREDICTIVE;
+ type = KeyboardType::PREDICTIVE;
} else if (typeToken == "ALPHA") {
- type = KEYBOARD_TYPE_ALPHA;
+ type = KeyboardType::ALPHA;
} else if (typeToken == "FULL") {
- type = KEYBOARD_TYPE_FULL;
+ type = KeyboardType::FULL;
} else if (typeToken == "SPECIAL_FUNCTION") {
ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set "
"the property 'keyboard.specialFunction' to '1' there instead.");
// TODO: return BAD_VALUE here in Q
- type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
+ type = KeyboardType::SPECIAL_FUNCTION;
} else if (typeToken == "OVERLAY") {
- type = KEYBOARD_TYPE_OVERLAY;
+ type = KeyboardType::OVERLAY;
} else {
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
typeToken.string());
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 38a68b3..14dc9e5 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -128,7 +128,7 @@
}
base::Result<std::shared_ptr<KeyCharacterMap>> ret =
- KeyCharacterMap::load(path, KeyCharacterMap::FORMAT_BASE);
+ KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
if (!ret) {
return ret.error().code();
}
@@ -159,9 +159,9 @@
bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
// TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q
- if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration)
- || keyMap->keyCharacterMap->getKeyboardType()
- == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
+ if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) ||
+ keyMap->keyCharacterMap->getKeyboardType() ==
+ KeyCharacterMap::KeyboardType::SPECIAL_FUNCTION) {
return false;
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb967ce..cd7f37b 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -78,6 +78,7 @@
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
"skia/filters/BlurFilter.cpp",
+ "skia/filters/LinearEffect.cpp",
],
}
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
index b9cea10..245c9e1 100644
--- a/libs/renderengine/Description.cpp
+++ b/libs/renderengine/Description.cpp
@@ -52,5 +52,10 @@
return colorMatrix != identity;
}
+bool Description::hasDisplayColorMatrix() const {
+ const mat4 identity;
+ return displayColorMatrix != identity;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 13577f7..be83ebc 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1121,6 +1121,7 @@
setOutputDataSpace(display.outputDataspace);
setDisplayMaxLuminance(display.maxLuminance);
+ setDisplayColorTransform(display.colorTransform);
const mat4 projectionMatrix =
ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
@@ -1189,7 +1190,7 @@
position[3] = vec2(bounds.right, bounds.top);
setupLayerCropping(*layer, mesh);
- setColorTransform(display.colorTransform * layer->colorTransform);
+ setColorTransform(layer->colorTransform);
bool usePremultipliedAlpha = true;
bool disableTexture = true;
@@ -1351,6 +1352,10 @@
mState.colorMatrix = colorTransform;
}
+void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) {
+ mState.displayColorMatrix = colorTransform;
+}
+
void GLESRenderEngine::disableTexturing() {
mState.textureEnabled = false;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 1779994..c0449a1 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -168,6 +168,7 @@
void setupLayerTexturing(const Texture& texture);
void setupFillWithColor(float r, float g, float b, float a);
void setColorTransform(const mat4& colorTransform);
+ void setDisplayColorTransform(const mat4& colorTransform);
void disableTexturing();
void disableBlending();
void setupCornerRadiusCropSize(float width, float height);
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index f4fbf35..a172c56 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -66,6 +66,7 @@
mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
+ mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix");
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
@@ -129,6 +130,9 @@
const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
glUniform4fv(mColorLoc, 1, color);
}
+ if (mDisplayColorMatrixLoc >= 0) {
+ glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray());
+ }
if (mInputTransformMatrixLoc >= 0) {
mat4 inputTransformMatrix = desc.inputTransformMatrix;
glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index fc3755e..4292645 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -104,6 +104,7 @@
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
GLint mOutputTransformMatrixLoc;
+ GLint mDisplayColorMatrixLoc;
/* location of corner radius uniform */
GLint mCornerRadiusLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 3ae35ec..7fc0499 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -180,6 +180,9 @@
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
+ description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
+ : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
.set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
@@ -661,7 +664,8 @@
)__SHADER__";
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
fs << "uniform float maxMasteringLuminance;";
@@ -700,6 +704,21 @@
)__SHADER__";
}
+ if (needs.hasDisplayColorMatrix()) {
+ fs << "uniform mat4 displayColorMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ }
+
generateEOTF(fs, needs);
generateOOTF(fs, needs);
generateOETF(fs, needs);
@@ -732,14 +751,17 @@
}
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
if (!needs.isOpaque() && needs.isPremultiplied()) {
// un-premultiply if needed before linearization
// avoid divide by 0 by adding 0.5/256 to the alpha channel
fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
}
fs << "gl_FragColor.rgb = "
- "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+ "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))"
+ ")));";
+
if (!needs.isOpaque() && needs.isPremultiplied()) {
// and re-premultiply if needed after gamma correction
fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 901e631..37bb651 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -117,6 +117,11 @@
SHADOW_MASK = 1 << SHADOW_SHIFT,
SHADOW_OFF = 0 << SHADOW_SHIFT,
SHADOW_ON = 1 << SHADOW_SHIFT,
+
+ DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
};
inline Key() : mKey(0) {}
@@ -143,6 +148,10 @@
inline bool hasOutputTransformMatrix() const {
return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
}
+ inline bool hasDisplayColorMatrix() const {
+ return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) ==
+ DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
+ }
inline bool hasTransformMatrix() const {
return hasInputTransformMatrix() || hasOutputTransformMatrix();
}
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index a62161a..fa6ec10 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -44,6 +44,7 @@
bool hasInputTransformMatrix() const;
bool hasOutputTransformMatrix() const;
bool hasColorMatrix() const;
+ bool hasDisplayColorMatrix() const;
// whether textures are premultiplied
bool isPremultipliedAlpha = false;
@@ -79,6 +80,8 @@
// The color matrix will be applied in linear space right before OETF.
mat4 colorMatrix;
+ // The display color matrix will be applied in gamma space after OETF
+ mat4 displayColorMatrix;
mat4 inputTransformMatrix;
mat4 outputTransformMatrix;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index a9bacb7..1f25fbf 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -16,6 +16,9 @@
//#define LOG_NDEBUG 0
#include <cstdint>
+
+#include "SkImageInfo.h"
+#include "system/graphics-base-v1.0.h"
#undef LOG_TAG
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -50,6 +53,7 @@
#include "../gl/GLExtensions.h"
#include "SkiaGLRenderEngine.h"
#include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -411,6 +415,32 @@
matrix[3][3], 0);
}
+static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
+ int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
+ int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ // Treat unsupported dataspaces as srgb
+ if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+ destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+ destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+ destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+ }
+
+ if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+ sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+ sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+ sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+ }
+
+ const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+ const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+ const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+ const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+
+ return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
+ sourceTransfer != destTransfer;
+}
+
void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
std::lock_guard<std::mutex> lock(mRenderingMutex);
mImageCache.erase(bufferId);
@@ -544,14 +574,20 @@
if (iter != mImageCache.end()) {
image = iter->second;
} else {
- image = SkImage::MakeFromAHardwareBuffer(item.buffer->toAHardwareBuffer(),
- item.usePremultipliedAlpha
- ? kPremul_SkAlphaType
- : kUnpremul_SkAlphaType,
- mUseColorManagement
- ? toColorSpace(
- layer->sourceDataspace)
- : SkColorSpace::MakeSRGB());
+ image = SkImage::MakeFromAHardwareBuffer(
+ item.buffer->toAHardwareBuffer(),
+ item.isOpaque ? kOpaque_SkAlphaType
+ : (item.usePremultipliedAlpha ? kPremul_SkAlphaType
+ : kUnpremul_SkAlphaType),
+ mUseColorManagement
+ ? (needsToneMapping(layer->sourceDataspace, display.outputDataspace)
+ // If we need to map to linear space, then
+ // mark the source image with the same
+ // colorspace as the destination surface so
+ // that Skia's color management is a no-op.
+ ? toColorSpace(display.outputDataspace)
+ : toColorSpace(layer->sourceDataspace))
+ : SkColorSpace::MakeSRGB());
mImageCache.insert({item.buffer->getId(), image});
}
@@ -565,6 +601,10 @@
}
auto texMatrix = getSkM44(item.textureTransform).asM33();
+
+ // b/171404534, scale to fix the layer
+ matrix.postScale(bounds.getWidth() / bufferWidth, bounds.getHeight() / bufferHeight);
+
// textureTansform was intended to be passed directly into a shader, so when
// building the total matrix with the textureTransform we need to first
// normalize it, then apply the textureTransform, then scale back up.
@@ -595,7 +635,22 @@
matrix.postConcat(texMatrix);
matrix.postScale(rotatedBufferWidth, rotatedBufferHeight);
- paint.setShader(image->makeShader(matrix));
+ sk_sp<SkShader> shader = image->makeShader(matrix);
+
+ if (mUseColorManagement &&
+ needsToneMapping(layer->sourceDataspace, display.outputDataspace)) {
+ LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
+ .outputDataspace = display.outputDataspace,
+ .undoPremultipliedAlpha = !item.isOpaque &&
+ item.usePremultipliedAlpha};
+ sk_sp<SkRuntimeEffect> runtimeEffect = buildRuntimeEffect(effect);
+ paint.setShader(createLinearEffectShader(shader, effect, runtimeEffect,
+ display.maxLuminance,
+ layer->source.buffer.maxMasteringLuminance,
+ layer->source.buffer.maxContentLuminance));
+ } else {
+ paint.setShader(shader);
+ }
} else {
ATRACE_NAME("DrawColor");
const auto color = layer->source.solidColor;
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
new file mode 100644
index 0000000..376abdf
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2020 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 "LinearEffect.h"
+
+#include <SkString.h>
+
+#include <optional>
+
+#include "log/log.h"
+#include "math/mat4.h"
+#include "ui/ColorSpace.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 EOTF(float3 color) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+ tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return pow(tmp, 1.0 / float3(m1));
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateXYZTransforms(SkString& shader) {
+ shader.append(R"(
+ uniform float4x4 in_rgbToXyz;
+ uniform float4x4 in_xyzToRgb;
+ float3 ToXYZ(float3 rgb) {
+ return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
+ }
+
+ float3 ToRGB(float3 xyz) {
+ return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+ }
+ )");
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+ SkString& shader) {
+ shader.append(R"(
+ uniform float in_displayMaxLuminance;
+ uniform float in_inputMaxLuminance;
+ uniform float in_maxContentLuminance;
+ )");
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 10000.0;
+ }
+ )");
+
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ default:
+ shader.append(R"(
+ float3 ToneMap(float3 xyz) {
+ float maxInLumi = in_inputMaxLuminance;
+ float maxOutLumi = in_displayMaxLuminance;
+
+ float nits = xyz.y;
+
+ // clamp to max input luminance
+ nits = clamp(nits, 0.0, maxInLumi);
+
+ // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+ if (maxInLumi <= maxOutLumi) {
+ return xyz * (maxOutLumi / maxInLumi);
+ } else {
+ // three control points
+ const float x0 = 10.0;
+ const float y0 = 17.0;
+ float x1 = maxOutLumi * 0.75;
+ float y1 = x1;
+ float x2 = x1 + (maxInLumi - x1) / 2.0;
+ float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+ // horizontal distances between the last three control points
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
+ // tangents at the last three control points
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
+
+ if (nits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return xyz * slope;
+ } else if (nits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ float slope = (y1 - y0) / (x1 - x0);
+ nits = y0 + (nits - x0) * slope;
+ } else if (nits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ float t = (nits - x1) / h12;
+ nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+ (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+ } else {
+ // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+ float t = (nits - x2) / h23;
+ nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+ (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+ }
+ }
+
+ // color.y is greater than x0 and is thus non-zero
+ return xyz * (nits / xyz.y);
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_displayMaxLuminance;
+ }
+
+ float3 ToneMap(float3 xyz) {
+ return xyz;
+ }
+ )");
+ break;
+ }
+
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 10000.0;
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / in_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+
+ shader.append(R"(
+ float3 OOTF(float3 xyz) {
+ return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
+ }
+ )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 OETF(float3 xyz) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(xyz, float3(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float3(m2));
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
+ shader.append(R"(
+ in shader input;
+ half4 main(float2 xy) {
+ float4 c = float4(sample(input, xy));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb / (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb * (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ return c;
+ }
+ )");
+}
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ return ColorSpace::BT2020();
+ break;
+ default:
+ return ColorSpace::sRGB();
+ break;
+ }
+}
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+ SkString shaderString;
+ generateEOTF(linearEffect.inputDataspace, shaderString);
+ generateXYZTransforms(shaderString);
+ generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+ generateOETF(linearEffect.outputDataspace, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+
+ auto [shader, error] = SkRuntimeEffect::Make(shaderString);
+ if (!shader) {
+ LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+ }
+ return shader;
+}
+
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+ sk_sp<SkRuntimeEffect> runtimeEffect,
+ float maxDisplayLuminance, float maxMasteringLuminance,
+ float maxContentLuminance) {
+ SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
+
+ effectBuilder.child("input") = shader;
+
+ ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+ ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+
+ effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
+ effectBuilder.uniform("in_xyzToRgb") = mat4(outputColorSpace.getXYZtoRGB());
+ effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
+ effectBuilder.uniform("in_inputMaxLuminance") =
+ std::min(maxMasteringLuminance, maxContentLuminance);
+ return effectBuilder.makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
new file mode 100644
index 0000000..2615669
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "SkColorMatrix.h"
+#include "SkRuntimeEffect.h"
+#include "SkShader.h"
+#include "ui/GraphicTypes.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+ // Input dataspace of the source colors.
+ const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+ // Working dataspace for the output surface, for conversion from linear space.
+ const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+ // Sets whether alpha premultiplication must be undone.
+ // This is required if the source colors use premultiplied alpha and is not opaque.
+ const bool undoPremultipliedAlpha = false;
+};
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+
+// Generates a shader resulting from applying the a linear effect created from
+// LinearEffectARgs::buildEffect to an inputShader. We also provide additional HDR metadata upon
+// creating the shader:
+// * The max display luminance is the max luminance of the physical display in nits
+// * The max mastering luminance is provided as the max luminance from the SMPTE 2086
+// standard.
+// * The max content luminance is provided as the max light level from the CTA 861.3
+// standard.
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
+ const LinearEffect& linearEffect,
+ sk_sp<SkRuntimeEffect> runtimeEffect,
+ float maxDisplayLuminance, float maxMasteringLuminance,
+ float maxContentLuminance);
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d795616..d20fcc4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -39,7 +39,7 @@
struct RenderEngineTest : public ::testing::Test {
static void SetUpTestSuite() {
- sRE = renderengine::gl::GLESRenderEngine::create(
+ renderengine::RenderEngineCreationArgs reCreationArgs =
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
@@ -49,13 +49,18 @@
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
- .build());
+ .build();
+ sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+
+ reCreationArgs.useColorManagement = true;
+ sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
}
static void TearDownTestSuite() {
// The ordering here is important - sCurrentBuffer must live longer
// than RenderEngine to avoid a null reference on tear-down.
sRE = nullptr;
+ sRECM = nullptr;
sCurrentBuffer = nullptr;
}
@@ -85,6 +90,9 @@
sRE->deleteTextures(1, &texName);
EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
}
+ for (uint32_t texName : mTexNamesCM) {
+ sRECM->deleteTextures(1, &texName);
+ }
}
void writeBufferToFile(const char* basename) {
@@ -253,10 +261,11 @@
void invokeDraw(renderengine::DisplaySettings settings,
std::vector<const renderengine::LayerSettings*> layers,
- sp<GraphicBuffer> buffer) {
+ sp<GraphicBuffer> buffer, bool useColorManagement = false) {
base::unique_fd fence;
- status_t status =
- sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
+ status_t status = useColorManagement
+ ? sRECM->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence)
+ : sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
sCurrentBuffer = buffer;
int fd = fence.release();
@@ -267,7 +276,11 @@
ASSERT_EQ(NO_ERROR, status);
if (layers.size() > 0) {
- ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ if (useColorManagement) {
+ ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId()));
+ } else {
+ ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ }
}
}
@@ -322,12 +335,15 @@
void fillBufferLayerTransform();
template <typename SourceVariant>
- void fillBufferWithColorTransform();
+ void fillBufferWithColorTransform(bool useColorManagement = false);
template <typename SourceVariant>
void fillBufferColorTransform();
template <typename SourceVariant>
+ void fillBufferColorTransformCM();
+
+ template <typename SourceVariant>
void fillRedBufferWithRoundedCorners();
template <typename SourceVariant>
@@ -366,6 +382,8 @@
// For now, exercise the GL backend directly so that some caching specifics
// can be tested without changing the interface.
static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+ // renderengine object with Color Management enabled
+ static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM;
// Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
// be freed *after* RenderEngine is destroyed, so that the EGL image is
// destroyed first.
@@ -374,14 +392,17 @@
sp<GraphicBuffer> mBuffer;
std::vector<uint32_t> mTexNames;
+ std::vector<uint32_t> mTexNamesCM;
};
std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr;
+
sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
struct ColorSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* /*fixture*/) {
+ RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) {
layer.source.solidColor = half3(r, g, b);
}
};
@@ -409,11 +430,16 @@
template <typename OpaquenessVariant>
struct BufferSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* fixture) {
+ RenderEngineTest* fixture, bool useColorManagement = false) {
sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
uint32_t texName;
- fixture->sRE->genTextures(1, &texName);
- fixture->mTexNames.push_back(texName);
+ if (useColorManagement) {
+ fixture->sRECM->genTextures(1, &texName);
+ fixture->mTexNamesCM.push_back(texName);
+ } else {
+ fixture->sRE->genTextures(1, &texName);
+ fixture->mTexNames.push_back(texName);
+ }
uint8_t* pixels;
buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -643,7 +669,7 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferWithColorTransform() {
+void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
@@ -652,12 +678,12 @@
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement);
layer.alpha = 1.0f;
// construct a fake color matrix
// annihilate green and blue channels
- settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
// set red channel to red + green
layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
@@ -666,13 +692,19 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers, mBuffer, useColorManagement);
}
template <typename SourceVariant>
void RenderEngineTest::fillBufferColorTransform() {
fillBufferWithColorTransform<SourceVariant>();
- expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+ expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformCM() {
+ fillBufferWithColorTransform<SourceVariant>(true);
+ expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1);
}
template <typename SourceVariant>
@@ -1073,7 +1105,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
- fillBufferLayerTransform<ColorSourceVariant>();
+ fillBufferColorTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) {
+ fillBufferColorTransformCM<ColorSourceVariant>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
@@ -1129,7 +1165,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
@@ -1185,7 +1225,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index db9704d..24ba830 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -1,6 +1,5 @@
cc_library_headers {
name: "libpdx_headers",
- host_supported: true,
export_include_dirs: ["private"],
vendor_available: true,
min_sdk_version: "29",
@@ -8,7 +7,6 @@
cc_library_static {
name: "libpdx",
- host_supported: true,
clang: true,
cflags: [
"-Wall",
diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp
index 114cec9..b36e0de 100644
--- a/libs/vr/libpdx/fuzz/Android.bp
+++ b/libs/vr/libpdx/fuzz/Android.bp
@@ -42,7 +42,6 @@
cc_fuzz {
name: "libpdx_serialization_fuzzer",
- host_supported: true,
clang: true,
srcs: [
"serialization_fuzzer.cpp",
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
index 1703f48..f5c5a5a 100644
--- a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -108,8 +108,181 @@
Deserialize(&t1_val, &result);
}
+void FuzzDeserializeUint8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::uint8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_UINT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::int8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT64,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFloat32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FLOAT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ float floatResult;
+ Deserialize(&floatResult, &buffer);
+
+ buffer.Rewind();
+ double doubleResult;
+ Deserialize(&doubleResult, &buffer);
+}
+
+void FuzzDeserializeFloat64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_FLOAT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ double result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixstr(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ Payload buffer = {ENCODING_TYPE_FIXSTR_MAX};
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ std::string result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixmap(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ // Fill the map with the fuzzed data, not attempting to
+ // make a valid map
+ while (fdp.remaining_bytes() > 0) {
+ buffer.Append(1, fdp.ConsumeIntegral<uint8_t>());
+ }
+
+ std::map<std::uint32_t, std::uint32_t> result;
+ Deserialize(&result, &buffer);
+
+ buffer.Rewind();
+ std::unordered_map<std::uint32_t, std::uint32_t> unorderedResult;
+ Deserialize(&unorderedResult, &buffer);
+}
+
+void FuzzDeserializeVariant(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16,
+ ENCODING_TYPE_FLOAT32,
+ ENCODING_TYPE_FIXSTR_MAX,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ // Add the rest of the data as a string
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ Variant<int, float, std::string> result;
+ Deserialize(&result, &buffer);
+}
+
+// Attempts to deserialize fuzzed data as various types
+void FuzzDeserialize(const uint8_t* data, size_t size) {
+ FuzzDeserializeUint8(data, size);
+ FuzzDeserializeUint16(data, size);
+ FuzzDeserializeUint32(data, size);
+ FuzzDeserializeUint64(data, size);
+ FuzzDeserializeInt8(data, size);
+ FuzzDeserializeInt16(data, size);
+ FuzzDeserializeInt32(data, size);
+ FuzzDeserializeInt64(data, size);
+ FuzzDeserializeFloat32(data, size);
+ FuzzDeserializeFloat64(data, size);
+ FuzzDeserializeFixstr(data, size);
+ FuzzDeserializeFixmap(data, size);
+ FuzzDeserializeVariant(data, size);
+}
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzSerializeDeserialize(data, size);
+ FuzzDeserialize(data, size);
return 0;
}
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index b2dadf8..9cc777d 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,6 +19,9 @@
//#define LOG_NDEBUG 0
#include "InputReaderBase.h"
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
+#include "input/NamedEnum.h"
#include <android/log.h>
#include <android-base/stringprintf.h>
@@ -99,17 +102,19 @@
size_t count = 0;
std::optional<DisplayViewport> result = std::nullopt;
for (const DisplayViewport& currentViewport : mDisplays) {
- // Return the first match
+ // Return the first match, or the default display if we're looking for the internal viewport
if (currentViewport.type == type) {
- if (!result) {
+ if (!result ||
+ (type == ViewportType::INTERNAL &&
+ currentViewport.displayId == ADISPLAY_ID_DEFAULT)) {
result = std::make_optional(currentViewport);
}
count++;
}
}
if (count > 1) {
- ALOGE("Found %zu viewports with type %s, but expected 1 at most",
- count, viewportTypeToString(type));
+ ALOGW("Found %zu viewports with type %s, but expected 1 at most", count,
+ NamedEnum::string(type).c_str());
}
return result;
}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 2b18180..0661709 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -250,7 +250,7 @@
int32_t userActivityEventType;
uint32_t seq;
bool handled;
- std::shared_ptr<InputChannel> inputChannel;
+ sp<IBinder> connectionToken;
sp<IBinder> oldToken;
sp<IBinder> newToken;
std::string obscuringPackage;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3135c19..e5d208a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -204,12 +204,12 @@
return true;
}
-static void dumpRegion(std::string& dump, const Region& region) {
+static std::string dumpRegion(const Region& region) {
if (region.isEmpty()) {
- dump += "<empty>";
- return;
+ return "<empty>";
}
+ std::string dump;
bool first = true;
Region::const_iterator cur = region.begin();
Region::const_iterator const tail = region.end();
@@ -222,6 +222,7 @@
dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom);
cur++;
}
+ return dump;
}
static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
@@ -1229,9 +1230,7 @@
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
sp<IBinder> focusedWindowToken =
getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
- if (focusedWindowToken != nullptr) {
- commandEntry->inputChannel = getInputChannelLocked(focusedWindowToken);
- }
+ commandEntry->connectionToken = focusedWindowToken;
commandEntry->keyEntry = entry;
postCommandLocked(std::move(commandEntry));
return false; // wait for the command to run
@@ -2271,15 +2270,19 @@
std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
bool isTouchedWindow) const {
- return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", mode=%s, alpha=%.2f, "
+ return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32
+ ", mode=%s, alpha=%.2f, "
"frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
- "], window=%s, applicationInfo=%s, flags=%s\n",
+ "], touchableRegion=%s, window={%s}, applicationInfo=%s, "
+ "flags={%s}, inputFeatures={%s}, hasToken=%s\n",
(isTouchedWindow) ? "[TOUCHED] " : "",
- NamedEnum::string(info->type).c_str(), info->packageName.c_str(),
- info->ownerUid, toString(info->touchOcclusionMode).c_str(), info->alpha,
- info->frameLeft, info->frameTop, info->frameRight, info->frameBottom,
- info->name.c_str(), info->applicationInfo.name.c_str(),
- info->flags.string().c_str());
+ NamedEnum::string(info->type, "%" PRId32).c_str(),
+ info->packageName.c_str(), info->ownerUid, info->id,
+ toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
+ info->frameTop, info->frameRight, info->frameBottom,
+ dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
+ info->applicationInfo.name.c_str(), info->flags.string().c_str(),
+ info->inputFeatures.string().c_str(), toString(info->token != nullptr));
}
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
@@ -4400,6 +4403,24 @@
return dump;
}
+std::string InputDispatcher::dumpPendingFocusRequestsLocked() {
+ if (mPendingFocusRequests.empty()) {
+ return INDENT "mPendingFocusRequests: <none>\n";
+ }
+
+ std::string dump;
+ dump += INDENT "mPendingFocusRequests:\n";
+ for (const auto& [displayId, focusRequest] : mPendingFocusRequests) {
+ // Rather than printing raw values for focusRequest.token and focusRequest.focusedToken,
+ // try to resolve them to actual windows.
+ std::string windowName = getConnectionNameLocked(focusRequest.token);
+ std::string focusedWindowName = getConnectionNameLocked(focusRequest.focusedToken);
+ dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", token->%s, focusedToken->%s\n",
+ displayId, windowName.c_str(), focusedWindowName.c_str());
+ }
+ return dump;
+}
+
void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4422,6 +4443,7 @@
}
dump += dumpFocusedWindowsLocked();
+ dump += dumpPendingFocusRequestsLocked();
if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4465,32 +4487,34 @@
const sp<InputWindowHandle>& windowHandle = windowHandles[i];
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
+ dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
"portalToDisplayId=%d, paused=%s, focusable=%s, "
- "hasWallpaper=%s, visible=%s, "
+ "hasWallpaper=%s, visible=%s, alpha=%.2f, "
"flags=%s, type=0x%08x, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
"applicationInfo=%s, "
"touchableRegion=",
- i, windowInfo->name.c_str(), windowInfo->displayId,
- windowInfo->portalToDisplayId,
+ i, windowInfo->name.c_str(), windowInfo->id,
+ windowInfo->displayId, windowInfo->portalToDisplayId,
toString(windowInfo->paused),
toString(windowInfo->focusable),
toString(windowInfo->hasWallpaper),
- toString(windowInfo->visible),
+ toString(windowInfo->visible), windowInfo->alpha,
windowInfo->flags.string().c_str(),
static_cast<int32_t>(windowInfo->type),
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
windowInfo->applicationInfo.name.c_str());
- dumpRegion(dump, windowInfo->touchableRegion);
+ dump += dumpRegion(windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=%s",
windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
- "ms\n",
+ "ms, trustedOverlay=%s, hasToken=%s\n",
windowInfo->ownerPid, windowInfo->ownerUid,
- millis(windowInfo->dispatchingTimeout));
+ millis(windowInfo->dispatchingTimeout),
+ toString(windowInfo->trustedOverlay),
+ toString(windowInfo->token != nullptr));
windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
@@ -4827,6 +4851,14 @@
return nullptr;
}
+std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ return "<nullptr>";
+ }
+ return connection->getInputChannelName();
+}
+
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
removeByValue(mConnectionsByFd, connection);
@@ -4887,14 +4919,13 @@
connection.inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
-
- updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
- reason);
+ sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken();
+ updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = nullptr;
- commandEntry->inputChannel = connection.inputChannel;
+ commandEntry->connectionToken = connectionToken;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4908,7 +4939,6 @@
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = application;
- commandEntry->inputChannel = nullptr;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4977,10 +5007,8 @@
}
void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
- sp<IBinder> token =
- commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
mLock.unlock();
-
+ const sp<IBinder>& token = commandEntry->connectionToken;
const std::chrono::nanoseconds timeoutExtension =
mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
@@ -5043,9 +5071,7 @@
mLock.unlock();
android::base::Timer t;
- sp<IBinder> token = commandEntry->inputChannel != nullptr
- ? commandEntry->inputChannel->getConnectionToken()
- : nullptr;
+ const sp<IBinder>& token = commandEntry->connectionToken;
nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4565151..5387c40 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -210,6 +210,8 @@
sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
+ std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock);
+
void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
struct IBinderHash {
@@ -532,6 +534,7 @@
void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
void logDispatchStateLocked() REQUIRES(mLock);
std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
+ std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
// Registration.
void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ea84835..17f37c3 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3397,7 +3397,6 @@
void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
bool hovering) {
int32_t metaState = getContext()->getGlobalMetaState();
- int32_t displayId = mViewport.displayId;
if (down || hovering) {
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -3407,7 +3406,7 @@
} else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
}
- displayId = mPointerController->getDisplayId();
+ int32_t displayId = mPointerController->getDisplayId();
float xCursorPosition;
float yCursorPosition;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index dc32003..40471b2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -198,7 +198,7 @@
std::condition_variable mNotifyAnr;
std::chrono::nanoseconds mAnrTimeout = 0ms;
- virtual void notifyConfigurationChanged(nsecs_t when) override {
+ void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
}
@@ -213,17 +213,17 @@
return mAnrTimeout;
}
- virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
- virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
- virtual void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+ void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -241,22 +241,20 @@
return true;
}
- virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
- virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
- uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
- KeyEvent*) override {
+ bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
return false;
}
- virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags) override {
+ void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
/** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
* essentially a passthrough for notifySwitch.
@@ -264,13 +262,11 @@
mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
}
- virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t) override {}
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
- return false;
- }
+ bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
mOnPointerDownToken = newToken;
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 211b49e..a72d5c3 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -33,6 +33,8 @@
#include <math.h>
#include <memory>
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
namespace android {
@@ -1233,6 +1235,47 @@
}
/**
+ * When we have multiple internal displays make sure we always return the default display when
+ * querying by type.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) {
+ const std::string uniqueId1 = "uniqueId1";
+ const std::string uniqueId2 = "uniqueId2";
+ constexpr int32_t nonDefaultDisplayId = 2;
+ static_assert(nonDefaultDisplayId != ADISPLAY_ID_DEFAULT,
+ "Test display ID should not be ADISPLAY_ID_DEFAULT");
+
+ // Add the default display first and ensure it gets returned.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT,
+ ViewportType::INTERNAL);
+
+ std::optional<DisplayViewport> viewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(viewport);
+ ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+
+ // Add the default display second to make sure order doesn't matter.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT,
+ ViewportType::INTERNAL);
+
+ viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(viewport);
+ ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+}
+
+/**
* Check getDisplayViewportByPort
*/
TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 3cccaf9..6810c1b7 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -161,7 +161,7 @@
Mutex::Autolock _l(mConnectionLock);
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr ||
- !canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) ||
+ !canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ", mOpPackageName) ||
mSensorInfo.count(handle) > 0) {
return false;
}
@@ -460,8 +460,12 @@
mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
success = true;
} else {
+ int32_t sensorHandle = event.sensor;
+ String16 noteMsg("Sensor event (");
+ noteMsg.append(String16(mService->getSensorStringType(sensorHandle)));
+ noteMsg.append(String16(")"));
int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
- mOpPackageName);
+ mOpPackageName, {}, noteMsg);
success = (appOpMode == AppOpsManager::MODE_ALLOWED);
}
}
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 0ce32cc..85ce0f0 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -57,6 +57,12 @@
mNonSensor.getName());
}
+String8 SensorList::getStringType(int handle) const {
+ return getOne<String8>(
+ handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getStringType();},
+ mNonSensor.getStringType());
+}
+
sp<SensorInterface> SensorList::getInterface(int handle) const {
return getOne<sp<SensorInterface>>(
handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 8424b22..617ceef 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -53,6 +53,8 @@
const Vector<Sensor> getVirtualSensors() const;
String8 getName(int handle) const;
+ String8 getStringType(int handle) const;
+
sp<SensorInterface> getInterface(int handle) const;
bool isNewHandle(int handle) const;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 2969839..8f25bdb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1112,6 +1112,10 @@
return mSensors.getName(handle);
}
+String8 SensorService::getSensorStringType(int handle) const {
+ return mSensors.getStringType(handle);
+}
+
bool SensorService::isVirtualSensor(int handle) const {
sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->isVirtual();
@@ -1807,9 +1811,6 @@
}
const int32_t opCode = sensor.getRequiredAppOp();
- const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
- IPCThreadState::self()->getCallingUid(), opPackageName);
- bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
int targetSdkVersion = getTargetSdkVersion(opPackageName);
bool canAccess = false;
@@ -1822,14 +1823,16 @@
canAccess = true;
} else if (hasPermissionForSensor(sensor)) {
// Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
- if (opCode < 0 || appOpAllowed) {
+ if (opCode >= 0) {
+ const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
+ IPCThreadState::self()->getCallingUid(), opPackageName);
+ canAccess = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ } else {
canAccess = true;
}
}
- if (canAccess) {
- sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
- } else {
+ if (!canAccess) {
ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
operation, sensor.getName().string(), sensor.getRequiredPermission().string());
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 052cbfe..50c7c2f 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -295,6 +295,7 @@
virtual status_t dump(int fd, const Vector<String16>& args);
status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
String8 getSensorName(int handle) const;
+ String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d302f98..fa75ffa 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -393,6 +393,15 @@
nsecs_t expectedPresentTime) {
ATRACE_CALL();
+ // If this is not a valid vsync for the layer's uid, return and try again later
+ const bool isVsyncValidForUid =
+ mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
+ if (!isVsyncValidForUid) {
+ ATRACE_NAME("!isVsyncValidForUid");
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ return false;
+ }
+
bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
if (refreshRequired) {
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index ec828d4..71b05fd 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -442,7 +442,8 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
+ mName, mFrameTimelineVsyncId);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems.push_back({item, std::move(surfaceFrame)});
@@ -480,7 +481,8 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
+ mName, mFrameTimelineVsyncId);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems[mQueueItems.size() - 1].item = item;
mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index f851514..a64b243 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -277,7 +277,7 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
- postTime);
+ mOwnerUid, postTime);
desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
mCurrentState.desiredPresentTime = desiredPresentTime;
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 6ba4c43..e075d3e 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -5,10 +5,12 @@
"FrameTimeline.cpp",
],
shared_libs: [
+ "android.hardware.graphics.composer@2.4",
"libbase",
"libcutils",
"liblog",
"libgui",
+ "libtimestats",
"libui",
"libutils",
],
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 43176a3..bd87482 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -93,19 +93,19 @@
}
}
-std::string toString(JankType jankType) {
+std::string toString(TimeStats::JankType jankType) {
switch (jankType) {
- case JankType::None:
+ case TimeStats::JankType::None:
return "None";
- case JankType::Display:
+ case TimeStats::JankType::Display:
return "Composer/Display - outside SF and App";
- case JankType::SurfaceFlingerDeadlineMissed:
+ case TimeStats::JankType::SurfaceFlingerDeadlineMissed:
return "SurfaceFlinger Deadline Missed";
- case JankType::AppDeadlineMissed:
+ case TimeStats::JankType::AppDeadlineMissed:
return "App Deadline Missed";
- case JankType::PredictionExpired:
+ case TimeStats::JankType::PredictionExpired:
return "Prediction Expired";
- case JankType::SurfaceFlingerEarlyLatch:
+ case TimeStats::JankType::SurfaceFlingerEarlyLatch:
return "SurfaceFlinger Early Latch";
default:
return "Unclassified";
@@ -177,15 +177,19 @@
}
}
-SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState,
+SurfaceFrame::SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName,
+ std::string debugName, PredictionState predictionState,
frametimeline::TimelineItem&& predictions)
- : mLayerName(layerName),
+ : mOwnerPid(ownerPid),
+ mOwnerUid(ownerUid),
+ mLayerName(std::move(layerName)),
+ mDebugName(std::move(debugName)),
mPresentState(PresentState::Unknown),
mPredictionState(predictionState),
mPredictions(predictions),
mActuals({0, 0, 0}),
mActualQueueTime(0),
- mJankType(JankType::None),
+ mJankType(TimeStats::JankType::None),
mJankMetadata(0) {}
void SurfaceFrame::setPresentState(PresentState state) {
@@ -227,13 +231,13 @@
mActuals.presentTime = presentTime;
}
-void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
+void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) {
std::lock_guard<std::mutex> lock(mMutex);
mJankType = jankType;
mJankMetadata = jankMetadata;
}
-JankType SurfaceFrame::getJankType() const {
+TimeStats::JankType SurfaceFrame::getJankType() const {
std::lock_guard<std::mutex> lock(mMutex);
return mJankType;
}
@@ -267,13 +271,15 @@
void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
std::lock_guard<std::mutex> lock(mMutex);
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Layer - %s", mLayerName.c_str());
- if (mJankType != JankType::None) {
+ StringAppendF(&result, "Layer - %s", mDebugName.c_str());
+ if (mJankType != TimeStats::JankType::None) {
// Easily identify a janky Surface Frame in the dump
StringAppendF(&result, " [*] ");
}
StringAppendF(&result, "\n");
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
@@ -285,32 +291,37 @@
dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}
-FrameTimeline::FrameTimeline()
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
: mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
- mMaxDisplayFrames(kDefaultMaxDisplayFrames) {}
+ mMaxDisplayFrames(kDefaultMaxDisplayFrames),
+ mTimeStats(std::move(timeStats)) {}
FrameTimeline::DisplayFrame::DisplayFrame()
: surfaceFlingerPredictions(TimelineItem()),
surfaceFlingerActuals(TimelineItem()),
predictionState(PredictionState::None),
- jankType(JankType::None),
+ jankType(TimeStats::JankType::None),
jankMetadata(0) {
this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
}
std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- const std::string& layerName, std::optional<int64_t> token) {
+ pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ std::optional<int64_t> token) {
ATRACE_CALL();
if (!token) {
- return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None,
+ return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::None,
TimelineItem());
}
std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
if (predictions) {
- return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid,
+ return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Valid,
std::move(*predictions));
}
- return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired,
+ return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Expired,
TimelineItem());
}
@@ -359,6 +370,7 @@
}
}
if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+ int32_t totalJankReasons = TimeStats::JankType::None;
auto& displayFrame = pendingPresentFence.second;
displayFrame->surfaceFlingerActuals.presentTime = signalTime;
@@ -377,21 +389,26 @@
displayFrame->jankMetadata |= EarlyFinish;
}
- if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) {
- displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
- } else if (displayFrame->jankMetadata & LateFinish & LatePresent) {
- displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
+ if ((displayFrame->jankMetadata & EarlyFinish) &&
+ (displayFrame->jankMetadata & EarlyPresent)) {
+ displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
+ } else if ((displayFrame->jankMetadata & LateFinish) &&
+ (displayFrame->jankMetadata & LatePresent)) {
+ displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed;
} else if (displayFrame->jankMetadata & EarlyPresent ||
displayFrame->jankMetadata & LatePresent) {
// Cases where SF finished early but frame was presented late and vice versa
- displayFrame->jankType = JankType::Display;
+ displayFrame->jankType = TimeStats::JankType::Display;
}
}
+
if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
displayFrame->jankMetadata |=
sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
}
+ totalJankReasons |= displayFrame->jankType;
+
for (auto& surfaceFrame : displayFrame->surfaceFrames) {
if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
// Only presented SurfaceFrames need to be updated
@@ -401,13 +418,13 @@
const auto& predictionState = surfaceFrame->getPredictionState();
if (predictionState == PredictionState::Expired) {
// Jank analysis cannot be done on apps that don't use predictions
- surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
+ surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0);
continue;
} else if (predictionState == PredictionState::Valid) {
const auto& actuals = surfaceFrame->getActuals();
const auto& predictions = surfaceFrame->getPredictions();
int32_t jankMetadata = 0;
- JankType jankType = JankType::None;
+ TimeStats::JankType jankType = TimeStats::JankType::None;
if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
: EarlyFinish;
@@ -419,19 +436,26 @@
: EarlyPresent;
}
if (jankMetadata & EarlyPresent) {
- jankType = JankType::SurfaceFlingerEarlyLatch;
+ jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
} else if (jankMetadata & LatePresent) {
if (jankMetadata & EarlyFinish) {
// TODO(b/169890654): Classify this properly
- jankType = JankType::Display;
+ jankType = TimeStats::JankType::Display;
} else {
- jankType = JankType::AppDeadlineMissed;
+ jankType = TimeStats::JankType::AppDeadlineMissed;
}
}
+
+ totalJankReasons |= jankType;
+ mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
+ surfaceFrame->getName(),
+ jankType | displayFrame->jankType);
surfaceFrame->setJankInfo(jankType, jankMetadata);
}
}
}
+
+ mTimeStats->incrementJankyFrames(totalJankReasons);
}
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -467,7 +491,7 @@
void FrameTimeline::dumpDisplayFrame(std::string& result,
const std::shared_ptr<DisplayFrame>& displayFrame,
nsecs_t baseTime) {
- if (displayFrame->jankType != JankType::None) {
+ if (displayFrame->jankType != TimeStats::JankType::None) {
// Easily identify a janky Display Frame in the dump
StringAppendF(&result, " [*] ");
}
@@ -501,11 +525,11 @@
nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
for (size_t i = 0; i < mDisplayFrames.size(); i++) {
const auto& displayFrame = mDisplayFrames[i];
- if (displayFrame->jankType == JankType::None) {
+ if (displayFrame->jankType == TimeStats::JankType::None) {
// Check if any Surface Frame has been janky
bool isJanky = false;
for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
- if (surfaceFrame->getJankType() != JankType::None) {
+ if (surfaceFrame->getJankType() != TimeStats::JankType::None) {
isJanky = true;
break;
}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index bd637df..e61567e 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -16,9 +16,7 @@
#pragma once
-#include <deque>
-#include <mutex>
-
+#include <../TimeStats/TimeStats.h>
#include <gui/ISurfaceComposer.h>
#include <ui/FenceTime.h>
#include <utils/RefBase.h>
@@ -26,26 +24,10 @@
#include <utils/Timers.h>
#include <utils/Vector.h>
-namespace android::frametimeline {
+#include <deque>
+#include <mutex>
-/*
- * The type of jank that is associated with a Display/Surface frame
- */
-enum class JankType {
- // No Jank
- None,
- // Jank not related to SurfaceFlinger or the App
- Display,
- // SF took too long on the CPU
- SurfaceFlingerDeadlineMissed,
- // Either App or GPU took too long on the frame
- AppDeadlineMissed,
- // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank
- // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
- PredictionExpired,
- // Latching a buffer early might cause an early present of the frame
- SurfaceFlingerEarlyLatch,
-};
+namespace android::frametimeline {
enum JankMetadata {
// Frame was presented earlier than expected
@@ -125,6 +107,7 @@
virtual nsecs_t getActualQueueTime() const = 0;
virtual PresentState getPresentState() const = 0;
virtual PredictionState getPredictionState() const = 0;
+ virtual pid_t getOwnerPid() const = 0;
virtual void setPresentState(PresentState state) = 0;
@@ -147,8 +130,10 @@
// Create a new surface frame, set the predictions based on a token and return it to the caller.
// Sets the PredictionState of SurfaceFrame.
+ // Debug name is the human-readable debugging string for dumpsys.
virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken(
- const std::string& layerName, std::optional<int64_t> token) = 0;
+ pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ std::optional<int64_t> token) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -206,8 +191,8 @@
class SurfaceFrame : public android::frametimeline::SurfaceFrame {
public:
- SurfaceFrame(const std::string& layerName, PredictionState predictionState,
- TimelineItem&& predictions);
+ SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ PredictionState predictionState, TimelineItem&& predictions);
~SurfaceFrame() = default;
TimelineItem getPredictions() const override { return mPredictions; };
@@ -215,38 +200,46 @@
nsecs_t getActualQueueTime() const override;
PresentState getPresentState() const override;
PredictionState getPredictionState() const override { return mPredictionState; };
+ pid_t getOwnerPid() const override { return mOwnerPid; };
+ TimeStats::JankType getJankType() const;
+ nsecs_t getBaseTime() const;
+ uid_t getOwnerUid() const { return mOwnerUid; };
+ const std::string& getName() const { return mLayerName; };
void setActualStartTime(nsecs_t actualStartTime) override;
void setActualQueueTime(nsecs_t actualQueueTime) override;
void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
void setPresentState(PresentState state) override;
void setActualPresentTime(nsecs_t presentTime);
- void setJankInfo(JankType jankType, int32_t jankMetadata);
- JankType getJankType() const;
- nsecs_t getBaseTime() const;
+ void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata);
+
// All the timestamps are dumped relative to the baseTime
void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
private:
+ const pid_t mOwnerPid;
+ const uid_t mOwnerUid;
const std::string mLayerName;
+ const std::string mDebugName;
PresentState mPresentState GUARDED_BY(mMutex);
const PredictionState mPredictionState;
const TimelineItem mPredictions;
TimelineItem mActuals GUARDED_BY(mMutex);
nsecs_t mActualQueueTime GUARDED_BY(mMutex);
mutable std::mutex mMutex;
- JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
+ TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
};
class FrameTimeline : public android::frametimeline::FrameTimeline {
public:
- FrameTimeline();
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats);
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
- const std::string& layerName, std::optional<int64_t> token) override;
+ pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ std::optional<int64_t> token) override;
void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame,
SurfaceFrame::PresentState state) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
@@ -278,7 +271,7 @@
std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
PredictionState predictionState;
- JankType jankType = JankType::None; // Enum for the type of jank
+ TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank
int32_t jankMetadata = 0x0; // Additional details about the jank
};
@@ -300,6 +293,7 @@
TokenManager mTokenManager;
std::mutex mMutex;
uint32_t mMaxDisplayFrames;
+ std::shared_ptr<TimeStats> mTimeStats;
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although this
// number doesn't represent any bounds on the number of surface frames that can go in a display
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 57493d2..9481966 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -150,9 +150,11 @@
if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
// If the system didn't send an ownerUid, use the callingUid for the ownerUid.
mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid);
+ mOwnerPid = args.metadata.getInt32(METADATA_OWNER_PID, mCallingPid);
} else {
// A create layer request from a non system request cannot specify the owner uid
mOwnerUid = mCallingUid;
+ mOwnerPid = mCallingPid;
}
}
@@ -900,7 +902,9 @@
: std::make_optional(stateToCommit->frameTimelineVsyncId);
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mTransactionName, vsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
+ mName, mTransactionName,
+ vsyncId);
surfaceFrame->setActualQueueTime(stateToCommit->postTime);
// For transactions we set the acquire fence time to the post time as we
// don't have a buffer. For BufferStateLayer it is overridden in
@@ -1739,7 +1743,7 @@
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
- getName().c_str(), newTimestamps->postedTime);
+ getName().c_str(), mOwnerUid, newTimestamps->postedTime);
mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
newTimestamps->acquireFence);
}
@@ -2491,12 +2495,6 @@
transformedLayerBounds.bottom = tmp;
}
- // Input coordinates should be in display coordinate space.
- info.frameLeft = transformedLayerBounds.left;
- info.frameTop = transformedLayerBounds.top;
- info.frameRight = transformedLayerBounds.right;
- info.frameBottom = transformedLayerBounds.bottom;
-
// Compute the correct transform to send to input. This will allow it to transform the
// input coordinates from display space into window space. Therefore, it needs to use the
// final layer frame to create the inverse transform. Since surface insets are added later,
@@ -2518,6 +2516,16 @@
inputTransform.set(translation.x, translation.y);
info.transform = inputTransform.inverse();
+ // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped.
+ // The frame should be the area the user sees on screen since it's used for occlusion
+ // detection.
+ Rect screenBounds = Rect{mScreenBounds};
+ transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds);
+ info.frameLeft = transformedLayerBounds.left;
+ info.frameTop = transformedLayerBounds.top;
+ info.frameRight = transformedLayerBounds.right;
+ info.frameBottom = transformedLayerBounds.bottom;
+
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = inputTransform.transform(info.touchableRegion);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5d6c7ea..b1ab9ec 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -855,6 +855,8 @@
uid_t getOwnerUid() { return mOwnerUid; }
+ pid_t getOwnerPid() { return mOwnerPid; }
+
// This layer is not a clone, but it's the parent to the cloned hierarchy. The
// variable mClonedChild represents the top layer that will be cloned so this
// layer will be the parent of mClonedChild.
@@ -1043,6 +1045,14 @@
// Can only be accessed with the SF state lock held.
std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
+ // The owner of the layer. If created from a non system process, it will be the calling uid.
+ // If created from a system process, the value can be passed in.
+ uid_t mOwnerUid;
+
+ // The owner pid of the layer. If created from a non system process, it will be the calling pid.
+ // If created from a system process, the value can be passed in.
+ pid_t mOwnerPid;
+
private:
virtual void setTransformHint(ui::Transform::RotationFlags) {}
@@ -1101,10 +1111,6 @@
pid_t mCallingPid;
uid_t mCallingUid;
- // The owner of the layer. If created from a non system process, it will be the calling uid.
- // If created from a system process, the value can be passed in.
- uid_t mOwnerUid;
-
// The current layer is a clone of mClonedFrom. This means that this layer will update it's
// properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
// this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index bf2a509..bf5be47 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -31,6 +31,8 @@
#include <android-base/stringprintf.h>
+#include <binder/IPCThreadState.h>
+
#include <cutils/compiler.h>
#include <cutils/sched_policy.h>
@@ -123,11 +125,12 @@
} // namespace
-EventThreadConnection::EventThreadConnection(EventThread* eventThread,
+EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
ResyncCallback resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
: resyncCallback(std::move(resyncCallback)),
mConfigChanged(configChanged),
+ mOwnerUid(callingUid),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -170,10 +173,12 @@
EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
android::frametimeline::TokenManager* tokenManager,
- InterceptVSyncsCallback interceptVSyncsCallback)
+ InterceptVSyncsCallback interceptVSyncsCallback,
+ ThrottleVsyncCallback throttleVsyncCallback)
: mVSyncSource(std::move(vsyncSource)),
mTokenManager(tokenManager),
mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
+ mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
mThreadName(mVSyncSource->getName()) {
mVSyncSource->setCallback(this);
@@ -216,8 +221,9 @@
sp<EventThreadConnection> EventThread::createEventConnection(
ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
- return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
- configChanged);
+ return new EventThreadConnection(const_cast<EventThread*>(this),
+ IPCThreadState::self()->getCallingUid(),
+ std::move(resyncCallback), configChanged);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -443,6 +449,11 @@
bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
const sp<EventThreadConnection>& connection) const {
+ const auto throttleVsync = [&] {
+ return mThrottleVsyncCallback &&
+ mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid);
+ };
+
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
return true;
@@ -458,12 +469,22 @@
case VSyncRequest::SingleSuppressCallback:
connection->vsyncRequest = VSyncRequest::None;
return false;
- case VSyncRequest::Single:
+ case VSyncRequest::Single: {
+ if (throttleVsync()) {
+ return false;
+ }
connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
return true;
+ }
case VSyncRequest::Periodic:
+ if (throttleVsync()) {
+ return false;
+ }
return true;
default:
+ // We don't throttle vsync if the app set a vsync request rate
+ // since there is no easy way to do that and this is a very
+ // rare case
return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index e42ca05..2e2d989 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,7 +81,7 @@
class EventThreadConnection : public BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, ResyncCallback,
+ EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
ISurfaceComposer::ConfigChanged configChanged);
virtual ~EventThreadConnection();
@@ -98,6 +98,8 @@
const ISurfaceComposer::ConfigChanged mConfigChanged =
ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
+ const uid_t mOwnerUid;
+
private:
virtual void onFirstRef();
EventThread* const mEventThread;
@@ -143,9 +145,10 @@
class EventThread : public android::EventThread, private VSyncSource::Callback {
public:
using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+ using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
- EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*,
- InterceptVSyncsCallback);
+ EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
+ ThrottleVsyncCallback);
~EventThread();
sp<EventThreadConnection> createEventConnection(
@@ -196,6 +199,7 @@
frametimeline::TokenManager* const mTokenManager;
const InterceptVSyncsCallback mInterceptVSyncsCallback;
+ const ThrottleVsyncCallback mThrottleVsyncCallback;
const char* const mThreadName;
std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 1343375..47a4f42 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -90,6 +90,7 @@
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+ mVsync.mScheduled = false;
}
mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
{targetWakeupTime, readyTime, vsyncTime}),
@@ -114,6 +115,10 @@
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
+ if (mVsync.mScheduled) {
+ mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+ mVsync.lastCallbackTime.count()});
+ }
}
void MessageQueue::waitMessage() {
@@ -147,13 +152,10 @@
if (mEvents) {
mEvents->requestNextVsync();
} else {
- const auto [workDuration, lastVsyncCallback] = [&] {
- std::lock_guard lock(mVsync.mutex);
- std::chrono::nanoseconds mWorkDurationNanos = mVsync.workDuration;
- return std::make_pair(mWorkDurationNanos.count(), mVsync.lastCallbackTime.count());
- }();
-
- mVsync.registration->schedule({workDuration, /*readyDuration=*/0, lastVsyncCallback});
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.mScheduled = true;
+ mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+ mVsync.lastCallbackTime.count()});
}
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 139b38e..99ce3a6 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -109,6 +109,7 @@
TracedOrdinal<std::chrono::nanoseconds> workDuration
GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+ bool mScheduled GUARDED_BY(mutex) = false;
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 7ab49a9..150f925 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -48,7 +48,7 @@
}
}
-std::string RefreshRateConfigs::Policy::toString() {
+std::string RefreshRateConfigs::Policy::toString() const {
return base::StringPrintf("default config ID: %d, allowGroupSwitching = %d"
", primary range: [%.2f %.2f], app request range: [%.2f %.2f]",
defaultConfig.value(), allowGroupSwitching, primaryRange.min,
@@ -433,10 +433,12 @@
// defaultConfig must be a valid config, and within the given refresh rate range.
auto iter = mRefreshRates.find(policy.defaultConfig);
if (iter == mRefreshRates.end()) {
+ ALOGE("Default config is not found.");
return false;
}
const RefreshRate& refreshRate = *iter->second;
if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
+ ALOGE("Default config is not in the primary range.");
return false;
}
return policy.appRequestRange.min <= policy.primaryRange.min &&
@@ -446,6 +448,7 @@
status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
std::lock_guard lock(mLock);
if (!isPolicyValid(policy)) {
+ ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
return BAD_VALUE;
}
Policy previousPolicy = *getCurrentPolicyLocked();
@@ -625,4 +628,36 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
+void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) {
+ if (refreshRateHz > 0 && refreshRateHz < 1) {
+ return;
+ }
+
+ std::lock_guard lock(mLock);
+ if (refreshRateHz != 0) {
+ mPreferredRefreshRateForUid[uid] = refreshRateHz;
+ } else {
+ mPreferredRefreshRateForUid.erase(uid);
+ }
+}
+
+int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
+ constexpr float kThreshold = 0.1f;
+ std::lock_guard lock(mLock);
+
+ const auto iter = mPreferredRefreshRateForUid.find(uid);
+ if (iter == mPreferredRefreshRateForUid.end()) {
+ return 1;
+ }
+
+ const auto refreshRateHz = iter->second;
+ const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz;
+ const auto numPeriodsRounded = std::round(numPeriods);
+ if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
+ return 1;
+ }
+
+ return static_cast<int>(numPeriods);
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 280ed62..8ff92a0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -166,7 +166,7 @@
}
bool operator!=(const Policy& other) const { return !(*this == other); }
- std::string toString();
+ std::string toString() const;
};
// Return code set*Policy() to indicate the current policy is unchanged.
@@ -311,6 +311,13 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
+ // Stores the preferred refresh rate that an app should run at.
+ // refreshRate == 0 means no preference.
+ void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock);
+
+ // Returns a divider for the current refresh rate
+ int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+
private:
friend class RefreshRateConfigsTest;
@@ -368,6 +375,8 @@
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
+ std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock);
+
// The min and max refresh rates supported by the device.
// This will not change at runtime.
const RefreshRate* mMinSupportedRefreshRate;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7b8448f..a14019e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -212,13 +212,26 @@
readyDuration, traceVsync, name);
}
+bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
+ const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+ if (divider <= 1) {
+ return true;
+ }
+
+ return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider);
+}
+
Scheduler::ConnectionHandle Scheduler::createConnection(
const char* connectionName, frametimeline::TokenManager* tokenManager,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
+ auto throttleVsync = [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ return !isVsyncValid(expectedVsyncTimestamp, uid);
+ };
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
- std::move(interceptCallback));
+ std::move(interceptCallback),
+ std::move(throttleVsync));
return createConnection(std::move(eventThread));
}
@@ -379,7 +392,8 @@
auto eventThread =
std::make_unique<impl::EventThread>(std::move(vsyncSource),
/*tokenManager=*/nullptr,
- impl::EventThread::InterceptVSyncsCallback());
+ impl::EventThread::InterceptVSyncsCallback(),
+ impl::EventThread::ThrottleVsyncCallback());
mInjectorConnectionHandle = createConnection(std::move(eventThread));
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 47ce4a4..4c86d26 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -139,6 +139,10 @@
scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+ // Returns true if a given vsync timestamp is considered valid vsync
+ // for a given uid
+ bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
+
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index e90edf7..75d1e6f 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -27,6 +27,9 @@
#include <chrono>
#include <sstream>
+#undef LOG_TAG
+#define LOG_TAG "VSyncPredictor"
+
namespace android::scheduler {
using base::StringAppendF;
@@ -66,7 +69,7 @@
nsecs_t VSyncPredictor::currentPeriod() const {
std::lock_guard lock(mMutex);
- return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+ return mRateMap.find(mIdealPeriod)->second.slope;
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
@@ -118,7 +121,7 @@
// normalizing to the oldest timestamp cuts down on error in calculating the intercept.
auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
auto it = mRateMap.find(mIdealPeriod);
- auto const currentPeriod = std::get<0>(it->second);
+ auto const currentPeriod = it->second.slope;
// TODO (b/144707443): its important that there's some precision in the mean of the ordinals
// for the intercept calculation, so scale the ordinals by 1000 to continue
// fixed point calculation. Explore expanding
@@ -172,10 +175,8 @@
return true;
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
- std::lock_guard lock(mMutex);
-
- auto const [slope, intercept] = getVSyncPredictionModel(lock);
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+ auto const [slope, intercept] = getVSyncPredictionModelLocked();
if (mTimestamps.empty()) {
traceInt64If("VSP-mode", 1);
@@ -210,13 +211,71 @@
return prediction;
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
std::lock_guard lock(mMutex);
- return VSyncPredictor::getVSyncPredictionModel(lock);
+ return nextAnticipatedVSyncTimeFromLocked(timePoint);
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
- std::lock_guard<std::mutex> const&) const {
+/*
+ * Returns whether a given vsync timestamp is in phase with a vsync divider.
+ * For example, if the vsync timestamps are (0,16,32,48):
+ * isVSyncInPhase(0, 2) = true
+ * isVSyncInPhase(16, 2) = false
+ * isVSyncInPhase(32, 2) = true
+ */
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
+ struct VsyncError {
+ nsecs_t vsyncTimestamp;
+ float error;
+
+ bool operator<(const VsyncError& other) const { return error < other.error; }
+ };
+
+ std::lock_guard lock(mMutex);
+ if (divider <= 1) {
+ return true;
+ }
+
+ const nsecs_t period = mRateMap[mIdealPeriod].slope;
+ const nsecs_t justBeforeTimePoint = timePoint - period / 2;
+ const nsecs_t dividedPeriod = mIdealPeriod / divider;
+
+ // If this is the first time we have asked about this divider with the
+ // current vsync period, it is considered in phase and we store the closest
+ // vsync timestamp
+ const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod);
+ if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) {
+ const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
+ mRateDividerKnownTimestampMap[dividedPeriod] = vsync;
+ return true;
+ }
+
+ // Find the next N vsync timestamp where N is the divider.
+ // One of these vsyncs will be in phase. We return the one which is
+ // the most aligned with the last known in phase vsync
+ std::vector<VsyncError> vsyncs(static_cast<size_t>(divider));
+ const nsecs_t knownVsync = knownTimestampIter->second;
+ nsecs_t point = justBeforeTimePoint;
+ for (size_t i = 0; i < divider; i++) {
+ const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
+ const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider);
+ const auto error = std::abs(std::round(numPeriods) - numPeriods);
+ vsyncs[i] = {vsync, error};
+ point = vsync + 1;
+ }
+
+ const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
+ mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
+ return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
+ std::lock_guard lock(mMutex);
+ const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
+ return {model.slope, model.intercept};
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
return mRateMap.find(mIdealPeriod)->second;
}
@@ -269,8 +328,8 @@
for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
StringAppendF(&result,
"\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
- idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
- std::get<1>(periodInterceptTuple));
+ idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f,
+ periodInterceptTuple.intercept);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 5f2ec49..381cf81 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,10 +38,10 @@
uint32_t outlierTolerancePercent);
~VSyncPredictor();
- bool addVsyncTimestamp(nsecs_t timestamp) final;
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
- nsecs_t currentPeriod() const final;
- void resetModel() final;
+ bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+ nsecs_t currentPeriod() const final EXCLUDES(mMutex);
+ void resetModel() final EXCLUDES(mMutex);
/*
* Inform the model that the period is anticipated to change to a new value.
@@ -50,16 +50,23 @@
*
* \param [in] period The new period that should be used.
*/
- void setPeriod(nsecs_t period) final;
+ void setPeriod(nsecs_t period) final EXCLUDES(mMutex);
/* Query if the model is in need of more samples to make a prediction.
* \return True, if model would benefit from more samples, False if not.
*/
- bool needsMoreSamples() const final;
+ bool needsMoreSamples() const final EXCLUDES(mMutex);
- std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+ struct Model {
+ nsecs_t slope;
+ nsecs_t intercept;
+ };
- void dump(std::string& result) const final;
+ VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
+
+ bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex);
+
+ void dump(std::string& result) const final EXCLUDES(mMutex);
private:
VSyncPredictor(VSyncPredictor const&) = delete;
@@ -76,13 +83,19 @@
std::mutex mutable mMutex;
size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
- std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
- REQUIRES(mMutex);
+
+ Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
+
+ nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
nsecs_t mIdealPeriod GUARDED_BY(mMutex);
std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
- std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+ // Map between ideal vsync period and the calculated model
+ std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
+
+ // Map between the divided vsync period and the last known vsync timestamp
+ std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex);
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 107c540..2cd9b3d 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -68,6 +68,14 @@
virtual bool needsMoreSamples() const = 0;
+ /*
+ * Checks if a vsync timestamp is in phase for a given divider.
+ *
+ * \param [in] timePoint A vsync timestamp
+ * \param [in] divider The divider to check for
+ */
+ virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fa15d60..b8b2fbc 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -331,7 +331,7 @@
mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(std::make_unique<FrameTracer>()),
- mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>()),
+ mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -3787,9 +3787,7 @@
}
if (what & layer_state_t::eFrameTimelineVsyncChanged) {
layer->setFrameTimelineVsyncForTransaction(s.frameTimelineVsyncId, postTime);
- } else {
- // TODO (b/171252403) We are calling this too much, potentially triggering
- // unnecessary work
+ } else if (frameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) {
layer->setFrameTimelineVsyncForTransaction(frameTimelineVsyncId, postTime);
}
if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -4915,7 +4913,7 @@
}
// Numbers from 1000 to 1038 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1038) {
+ if (code >= 1000 && code <= 1039) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5280,6 +5278,13 @@
mFrameTimeline->setMaxDisplayFrames(n);
return NO_ERROR;
}
+ case 1039: {
+ // The first parameter is the uid
+ n = data.readInt32();
+ const float refreshRateHz = data.readFloat();
+ mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz);
+ }
+ return NO_ERROR;
}
}
return err;
@@ -5660,7 +5665,8 @@
});
}
- const uint32_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
(hasProtectedLayer && allowProtected && supportsProtected
? GRALLOC_USAGE_PROTECTED
: GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 37194c6..fe9e737 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
#undef LOG_TAG
#define LOG_TAG "TimeStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -33,6 +30,8 @@
#include <algorithm>
#include <chrono>
+#include "timestatsproto/TimeStatsHelper.h"
+
namespace android {
namespace impl {
@@ -115,6 +114,13 @@
mMaxPulledHistogramBuckets);
mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
renderEngineTimingBytes.size());
+
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalFrames);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalJankyFrames);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongCpu);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongGpu);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFUnattributed);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalAppUnattributed);
mStatsDelegate->statsEventBuild(event);
clearGlobalLocked();
@@ -160,6 +166,13 @@
mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+ mStatsDelegate->statsEventWriteInt32(event, layer->uid);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed);
mStatsDelegate->statsEventBuild(event);
}
@@ -397,11 +410,13 @@
timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
if (prevTimeRecord.ready) {
+ uid_t uid = layerRecord.uid;
const std::string& layerName = layerRecord.layerName;
- if (!mTimeStats.stats.count(layerName)) {
- mTimeStats.stats[layerName].layerName = layerName;
+ if (!mTimeStats.stats.count({uid, layerName})) {
+ mTimeStats.stats[{uid, layerName}].uid = uid;
+ mTimeStats.stats[{uid, layerName}].layerName = layerName;
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
@@ -462,8 +477,13 @@
layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
}
+bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
+ return mTimeStats.stats.count({uid, layerName}) > 0 ||
+ mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+}
+
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- nsecs_t postTime) {
+ uid_t uid, nsecs_t postTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -471,11 +491,12 @@
postTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
+ if (!canAddNewAggregatedStats(uid, layerName)) {
return;
}
if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
layerNameIsValid(layerName)) {
+ mTimeStatsTracker[layerId].uid = uid;
mTimeStatsTracker[layerId].layerName = layerName;
}
if (!mTimeStatsTracker.count(layerId)) return;
@@ -655,6 +676,66 @@
flushAvailableRecordsToStatsLocked(layerId);
}
+template <class T>
+static void updateJankPayload(T& t, int32_t reasons) {
+ t.jankPayload.totalFrames++;
+
+ static const constexpr int32_t kValidJankyReason =
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed |
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed |
+ TimeStats::JankType::AppDeadlineMissed | TimeStats::JankType::Display;
+ if (reasons & kValidJankyReason) {
+ t.jankPayload.totalJankyFrames++;
+ if ((reasons & TimeStats::JankType::SurfaceFlingerDeadlineMissed) != 0) {
+ t.jankPayload.totalSFLongCpu++;
+ }
+ if ((reasons & TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
+ t.jankPayload.totalSFLongGpu++;
+ }
+ if ((reasons & TimeStats::JankType::Display) != 0) {
+ t.jankPayload.totalSFUnattributed++;
+ }
+ if ((reasons & TimeStats::JankType::AppDeadlineMissed) != 0) {
+ t.jankPayload.totalAppUnattributed++;
+ }
+ }
+}
+
+void TimeStats::incrementJankyFrames(int32_t reasons) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ updateJankPayload<TimeStatsHelper::TimeStatsGlobal>(mTimeStats, reasons);
+}
+
+void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // Only update layer stats if we're allowed to do so.
+ // As an implementation detail, we do this because this method is expected to be
+ // called from FrameTimeline, which is allowed to do jank analysis well after a frame is
+ // presented. This means that we can't rely on TimeStats to flush layer records over to the
+ // aggregated stats.
+ if (!canAddNewAggregatedStats(uid, layerName)) {
+ return;
+ }
+
+ // Defensively initialize the stats in case FrameTimeline flushes its signaled present fences
+ // before TimeStats does.
+ if (!mTimeStats.stats.count({uid, layerName})) {
+ mTimeStats.stats[{uid, layerName}].uid = uid;
+ mTimeStats.stats[{uid, layerName}].layerName = layerName;
+ }
+
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
+ updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
+}
+
void TimeStats::onDestroy(int32_t layerId) {
ATRACE_CALL();
ALOGV("[%d]-onDestroy", layerId);
@@ -860,6 +941,7 @@
mTimeStats.presentToPresent.hist.clear();
mTimeStats.frameDuration.hist.clear();
mTimeStats.renderEngineTiming.hist.clear();
+ mTimeStats.jankPayload = TimeStatsHelper::JankPayload();
mTimeStats.refreshRateStats.clear();
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
@@ -905,6 +987,3 @@
} // namespace impl
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8de5d0c..4fa0a02 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,6 +17,7 @@
#pragma once
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <cstdint>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -85,7 +86,7 @@
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- nsecs_t postTime) = 0;
+ uid_t uid, nsecs_t postTime) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
@@ -108,6 +109,40 @@
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) = 0;
+
+ // Subset of jank metadata tracked by FrameTimeline for the purpose of funneling to telemetry.
+ enum JankType {
+ // No Jank
+ None = 0x0,
+ // Jank not related to SurfaceFlinger or the App
+ Display = 0x1,
+ // SF took too long on the CPU
+ SurfaceFlingerDeadlineMissed = 0x2,
+ // SF took too long on the GPU
+ SurfaceFlingerGpuDeadlineMissed = 0x4,
+ // Either App or GPU took too long on the frame
+ AppDeadlineMissed = 0x8,
+ // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
+ // jank
+ // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+ PredictionExpired = 0x10,
+ // Latching a buffer early might cause an early present of the frame
+ SurfaceFlingerEarlyLatch = 0x20,
+ };
+
+ // Increments janky frames, tracked globally. Because FrameTimeline is the infrastructure
+ // responsible for computing jank in the system, this is expected to be called from
+ // FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there are no
+ // jank reasons, then total frames are incremented but jank is not, for accurate accounting of
+ // janky frames.
+ virtual void incrementJankyFrames(int32_t reasons) = 0;
+ // Increments janky frames, blamed to the provided {uid, layerName} key, with JankMetadata as
+ // supplementary reasons for the jank. Because FrameTimeline is the infrastructure responsible
+ // for computing jank in the system, this is expected to be called from FrameTimeline, rather
+ // than directly from SurfaceFlinger or individual layers.
+ // If there are no jank reasons, then total frames are incremented but jank is not, for accurate
+ // accounting of janky frames.
+ virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0;
// Clean up the layer record
virtual void onDestroy(int32_t layerId) = 0;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -142,6 +177,7 @@
};
struct LayerRecord {
+ uid_t uid;
std::string layerName;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
@@ -241,7 +277,7 @@
void recordRenderEngineDuration(nsecs_t startTime,
const std::shared_ptr<FenceTime>& readyFence) override;
- void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
+ void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
nsecs_t postTime) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
@@ -253,6 +289,8 @@
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) override;
+ void incrementJankyFrames(int32_t reasons) override;
+ void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override;
// Clean up the layer record
void onDestroy(int32_t layerId) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -276,6 +314,7 @@
void flushAvailableRecordsToStatsLocked(int32_t layerId);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
+ bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
void enable();
void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index b937f41..9513cab 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -30,3 +30,15 @@
"-Wno-unused-parameter",
],
}
+
+// ==== java host library for timestats proto ===========================
+// Note timestats is deprecated and is only used for legacy tests
+java_library_host {
+ name: "host-timestats-proto",
+ srcs: [
+ "timestats.proto",
+ ],
+ proto: {
+ type: "full",
+ },
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d77387a..0fb748f 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -77,14 +77,28 @@
return result;
}
+std::string TimeStatsHelper::JankPayload::toString() const {
+ std::string result;
+ StringAppendF(&result, "totalTimelineFrames = %d\n", totalFrames);
+ StringAppendF(&result, "jankyFrames = %d\n", totalJankyFrames);
+ StringAppendF(&result, "sfLongCpuJankyFrames = %d\n", totalSFLongCpu);
+ StringAppendF(&result, "sfLongGpuJankyFrames = %d\n", totalSFLongGpu);
+ StringAppendF(&result, "sfUnattributedJankyFrame = %d\n", totalSFUnattributed);
+ StringAppendF(&result, "appUnattributedJankyFrame = %d\n", totalAppUnattributed);
+ return result;
+}
+
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
+ StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
+ result.append("Jank payload for this layer:\n");
+ result.append(jankPayload.toString());
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
const float averageTime = iter->second.averageTime();
@@ -110,6 +124,8 @@
StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches);
StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges);
StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+ result.append("Global aggregated jank payload:\n");
+ result.append(jankPayload.toString());
StringAppendF(&result, "displayConfigStats is as below:\n");
for (const auto& [fps, duration] : refreshRateStats) {
StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 0c75f96..033eb5d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -40,14 +40,28 @@
std::string toString() const;
};
+ struct JankPayload {
+ // note that transactions are counted for these frames.
+ int32_t totalFrames = 0;
+ int32_t totalJankyFrames = 0;
+ int32_t totalSFLongCpu = 0;
+ int32_t totalSFLongGpu = 0;
+ int32_t totalSFUnattributed = 0;
+ int32_t totalAppUnattributed = 0;
+
+ std::string toString() const;
+ };
+
class TimeStatsLayer {
public:
+ uid_t uid;
std::string layerName;
std::string packageName;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
int32_t badDesiredPresentFrames = 0;
+ JankPayload jankPayload;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
@@ -69,8 +83,17 @@
Histogram presentToPresent;
Histogram frameDuration;
Histogram renderEngineTiming;
- std::unordered_map<std::string, TimeStatsLayer> stats;
+
+ struct StatsHasher {
+ size_t operator()(const std::pair<uid_t, std::string>& p) const {
+ // Normally this isn't a very good hash function due to symmetry reasons,
+ // but these are distinct types so this should be good enough
+ return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+ }
+ };
+ std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats;
std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
+ JankPayload jankPayload;
std::string toString(std::optional<uint32_t> maxLayers) const;
SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 49cf80c..eee4bec 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -57,7 +57,9 @@
trace();
}
- operator T() const { return mData; }
+ T get() const { return mData; }
+
+ operator T() const { return get(); }
TracedOrdinal& operator=(T other) {
mData = other;
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 8d094e4..3535fbb 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -14,7 +14,6 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.graphics.composer@2.1-resources",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
@@ -34,6 +33,7 @@
"libutils",
],
static_libs: [
+ "android.hardware.graphics.composer@2.1-resources",
"libcompositionengine",
"libgmock",
"libperfetto_client_experimental",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index a4f7449..0911712 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -134,13 +134,15 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index db05d5a..f0311bd 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -70,12 +70,14 @@
void DisplayTransactionTest::injectMockScheduler() {
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index f680bdb..3aafd45 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -59,9 +59,11 @@
protected:
class MockEventThreadConnection : public EventThreadConnection {
public:
- MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
+ MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
+ ResyncCallback&& resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
- : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
+ : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
+ configChanged) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -73,7 +75,8 @@
void createThread(std::unique_ptr<VSyncSource>);
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
- ISurfaceComposer::ConfigChanged configChanged);
+ ISurfaceComposer::ConfigChanged configChanged,
+ uid_t ownerUid = mConnectionUid);
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
@@ -89,6 +92,7 @@
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
int32_t expectedConfigId,
nsecs_t expectedVsyncPeriod);
+ void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -96,12 +100,18 @@
mVSyncSetDurationCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
+ ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
MockVSyncSource* mVSyncSource;
VSyncSource::Callback* mCallback = nullptr;
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
+ sp<MockEventThreadConnection> mThrottledConnection;
+
+ static constexpr uid_t mConnectionUid = 443;
+ static constexpr uid_t mThrottledConnectionUid = 177;
};
EventThreadTest::EventThreadTest() {
@@ -124,6 +134,9 @@
createThread(std::move(vsyncSource));
mConnection = createConnection(mConnectionEventCallRecorder,
ISurfaceComposer::eConfigChangedDispatch);
+ mThrottledConnection =
+ createConnection(mThrottledConnectionEventCallRecorder,
+ ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid);
// A display must be connected for VSYNC events to be delivered.
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -140,9 +153,15 @@
}
void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+ const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
+ return (uid == mThrottledConnectionUid);
+ };
+
mThread = std::make_unique<impl::EventThread>(std::move(source),
/*tokenManager=*/nullptr,
- mInterceptVSyncCallRecorder.getInvocable());
+ mInterceptVSyncCallRecorder.getInvocable(),
+ throttleVsync);
// EventThread should register itself as VSyncSource callback.
mCallback = expectVSyncSetCallbackCallReceived();
@@ -150,10 +169,11 @@
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
- ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) {
+ ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged,
+ uid_t ownerUid) {
sp<MockEventThreadConnection> connection =
- new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
- configChanged);
+ new MockEventThreadConnection(mThread.get(), ownerUid,
+ mResyncCallRecorder.getInvocable(), configChanged);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -183,6 +203,13 @@
EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
}
+void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
+ auto args = mThrottleVsyncCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+ EXPECT_EQ(uid, std::get<1>(args.value()));
+}
+
void EventThreadTest::expectVsyncEventReceivedByConnection(
const char* name, ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount) {
@@ -267,13 +294,15 @@
// The interceptor should receive the event, as well as the connection.
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
- // The interceptor should receive the event, but the the connection should
+ // The interceptor should receive the event, but the connection should
// not as it was only interested in the first.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// EventThread should also detect that at this point that it does not need
@@ -323,16 +352,19 @@
// interceptor, and the connection.
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mConnectionUid);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
mCallback->onVSyncEvent(789, 777, 111);
expectInterceptCallReceived(789);
+ expectThrottleVsyncReceived(777, mConnectionUid);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -346,16 +378,19 @@
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The second event will be seen by the interceptor and the connection.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The third event will be seen by the interceptor, and not the connection.
mCallback->onVSyncEvent(789, 777, 744);
expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The fourth event will be seen by the interceptor and the connection.
mCallback->onVSyncEvent(101112, 7847, 86);
@@ -408,19 +443,19 @@
}
TEST_F(EventThreadTest, tracksEventConnections) {
- EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection =
createConnection(errorConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, errorConnection);
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
createConnection(secondConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, secondConnection);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
@@ -432,7 +467,7 @@
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
1u);
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
@@ -514,5 +549,35 @@
ASSERT_FALSE(args.has_value());
}
+TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+ // Signal that we want the next vsync event to be posted to the throttled connection
+ mThread->requestNextVsync(mThrottledConnection);
+
+ // EventThread should immediately request a resync.
+ EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+
+ // EventThread should enable vsync callbacks.
+ expectVSyncSetEnabledCallReceived(true);
+
+ // Use the received callback to signal a first vsync event.
+ // The interceptor should receive the event, but not the connection.
+ mCallback->onVSyncEvent(123, 456, 789);
+ expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mThrottledConnectionUid);
+ mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
+
+ // Use the received callback to signal a second vsync event.
+ // The interceptor should receive the event, but the connection should
+ // not as it was only interested in the first.
+ mCallback->onVSyncEvent(456, 123, 0);
+ expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mThrottledConnectionUid);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should not change the vsync state as it didn't send the event
+ // yet
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 69efd7f..03c6f70 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "gmock/gmock-spec-builders.h"
+#include "mock/MockTimeStats.h"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -23,6 +25,11 @@
#include <cinttypes>
using namespace std::chrono_literals;
+using testing::Contains;
+
+MATCHER_P(HasBit, bit, "") {
+ return (arg & bit) != 0;
+}
namespace android::frametimeline {
@@ -41,7 +48,8 @@
}
void SetUp() override {
- mFrameTimeline = std::make_unique<impl::FrameTimeline>();
+ mTimeStats = std::make_shared<mock::TimeStats>();
+ mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
mTokenManager = &mFrameTimeline->mTokenManager;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
@@ -76,6 +84,7 @@
return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
}
+ std::shared_ptr<mock::TimeStats> mTimeStats;
std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
impl::TokenManager* mTokenManager;
FenceToFenceTimeMap fenceFactory;
@@ -83,6 +92,12 @@
nsecs_t maxTokenRetentionTime;
};
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+static constexpr const uid_t sUidOne = 0;
+static constexpr pid_t sPidOne = 10;
+static constexpr pid_t sPidTwo = 20;
+
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
EXPECT_EQ(getPredictions().size(), 1);
@@ -98,22 +113,34 @@
EXPECT_EQ(compareTimelineItems(*predictions, TimelineItem(10, 20, 30)), true);
}
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
+ auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
+ EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
+ EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
+}
+
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
flushTokens(systemTime() + maxTokenRetentionTime);
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, token1);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, token1);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -125,7 +152,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, token1);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20);
@@ -148,8 +176,12 @@
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken1);
- auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken("layer2", surfaceFrameToken1);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
+ sLayerNameTwo, surfaceFrameToken1);
mFrameTimeline->setSfWakeUp(sfToken1, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
SurfaceFrame::PresentState::Presented);
@@ -168,7 +200,9 @@
// Trigger a flush by finalizing the next DisplayFrame
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- auto surfaceFrame3 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken2);
+ auto surfaceFrame3 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken2);
mFrameTimeline->setSfWakeUp(sfToken2, 52);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped);
mFrameTimeline->setSfPresent(56, presentFence2);
@@ -189,7 +223,9 @@
{10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
SurfaceFrame::PresentState::Presented);
@@ -209,7 +245,9 @@
{10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
@@ -224,7 +262,8 @@
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", std::nullopt);
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -232,7 +271,8 @@
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", std::nullopt);
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -244,7 +284,9 @@
// Size shouldn't exceed maxDisplayFrames - 64
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -258,7 +300,9 @@
EXPECT_EQ(*maxDisplayFrames, 256);
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -272,7 +316,9 @@
EXPECT_EQ(*maxDisplayFrames, 128);
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -281,4 +327,90 @@
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(sUidOne, sLayerNameOne,
+ HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->setSfWakeUp(sfToken1,
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ SurfaceFrame::PresentState::Presented);
+ presentFence1->signalForTest(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+ presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(TimeStats::JankType::Display)));
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::Display)));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->setSfWakeUp(sfToken1,
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ SurfaceFrame::PresentState::Presented);
+ presentFence1->signalForTest(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+ presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(sUidOne, sLayerNameOne,
+ HasBit(TimeStats::JankType::AppDeadlineMissed)));
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::AppDeadlineMissed)));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ surfaceFrame1->setAcquireFenceTime(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
+ mFrameTimeline->setSfWakeUp(sfToken1,
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ SurfaceFrame::PresentState::Presented);
+ presentFence1->signalForTest(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ presentFence1);
+}
+
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 1f6f166..4762fd4 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1471,6 +1471,34 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234));
+}
+
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+ const uid_t uid = 1234;
+ refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+ EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 2c8178e..8cd8372 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -131,12 +131,14 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index eee9400..509858a 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -42,7 +42,7 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, ResyncCallback(),
+ : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress) {}
~MockEventThreadConnection() = default;
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index efee826..e25d501 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -171,12 +171,14 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 0a24650..a90f424 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -58,6 +58,7 @@
#define FMT_STRING false
#define LAYER_ID_0 0
#define LAYER_ID_1 1
+#define UID_0 123
#define LAYER_ID_INVALID -1
#define NUM_LAYERS 1
#define NUM_LAYERS_INVALID "INVALID"
@@ -227,7 +228,8 @@
void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
switch (type) {
case TimeStamp::POST:
- ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts));
break;
case TimeStamp::ACQUIRE:
ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
@@ -349,6 +351,61 @@
EXPECT_THAT(result, HasSubstr(expectedResult));
}
+TEST_F(TimeStatsTest, canIncreaseJankyFrames) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(4);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(4);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
// this stat is not in the proto so verify by checking the string dump
constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -789,6 +846,16 @@
.count());
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
@@ -797,6 +864,11 @@
EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+ EXPECT_THAT(result, HasSubstr("jankyFrames = 0"));
+ EXPECT_THAT(result, HasSubstr("sfLongCpuJankyFrames = 0"));
+ EXPECT_THAT(result, HasSubstr("sfLongGpuJankyFrames = 0"));
+ EXPECT_THAT(result, HasSubstr("sfUnattributedJankyFrame = 0"));
+ EXPECT_THAT(result, HasSubstr("appUnattributedJankyFrame = 0"));
}
TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -904,6 +976,8 @@
mTimeStats->incrementClientCompositionFrames();
}
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->recordFrameDuration(1000000, 3000000);
@@ -913,6 +987,12 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
EXPECT_THAT(mDelegate->mAtomTags,
UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -944,6 +1024,12 @@
expectedRenderEngineTiming.c_str(),
expectedRenderEngineTiming.size()),
expectedRenderEngineTiming.size()));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
EXPECT_EQ(AStatsManager_PULL_SUCCESS,
@@ -975,6 +1061,15 @@
}
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
EXPECT_THAT(mDelegate->mAtomTags,
UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -1033,6 +1128,14 @@
EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
EXPECT_CALL(*mDelegate,
statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, UID_0));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
EXPECT_EQ(AStatsManager_PULL_SUCCESS,
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 760bf65..68cf330 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -66,13 +66,15 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 1e5139c..0af5f30 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -52,6 +52,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, int) const final { return false; }
void dump(std::string&) const final {}
private:
@@ -88,6 +89,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, int) const final { return false; }
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 69731fd..72b5396 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -48,6 +48,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index d4cd11d..3d60479 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -59,16 +59,16 @@
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ auto model = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(mPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ EXPECT_THAT(model.slope, Eq(mPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
auto const changedPeriod = 2000;
tracker.setPeriod(changedPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(changedPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(changedPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
@@ -264,17 +264,17 @@
}
auto const mMaxRoundingError = 100;
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
@@ -296,9 +296,9 @@
for (auto const& timestamp : simulatedVsyncsFast) {
tracker.addVsyncTimestamp(timestamp);
}
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
@@ -308,16 +308,16 @@
// we had a model for 100ns mPeriod before, use that until the new samples are
// sufficiently built up
tracker.setPeriod(idealPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
for (auto const& timestamp : simulatedVsyncsFast2) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod2));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod2));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
@@ -407,11 +407,9 @@
tracker.addVsyncTimestamp(i * realPeriod);
}
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(realPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(realPeriod, mMaxRoundingError));
tracker.resetModel();
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(idealPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(idealPeriod, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
@@ -450,6 +448,33 @@
EXPECT_THAT(intercept, Eq(0));
}
+TEST_F(VSyncPredictorTest, isVSyncInPhase) {
+ auto last = mNow;
+ auto const bias = 10;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod - bias;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ mNow += bias;
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+
+ const auto maxDivider = 5;
+ const auto maxPeriods = 15;
+ for (int divider = 1; divider < maxDivider; divider++) {
+ for (int i = 0; i < maxPeriods; i++) {
+ const bool expectedInPhase = (i % divider) == 0;
+ EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider))
+ << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
+ << (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
+ }
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 0dcaf26..a7568e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -42,6 +42,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index ff37ec8..99ec353 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -41,7 +41,7 @@
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+ MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t));
MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
@@ -50,6 +50,8 @@
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(incrementJankyFrames, void(int32_t));
+ MOCK_METHOD3(incrementJankyFrames, void(uid_t, const std::string&, int32_t));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
MOCK_METHOD1(setPowerMode,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 03ddc85..de98025 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -33,6 +33,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index 6589f74..0acff06 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -302,11 +302,13 @@
}
std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
+ b->ArgNames({"Effect", "Strength"});
+
if (supported.empty()) {
+ b->Args({static_cast<long>(-1), static_cast<long>(-1)});
return;
}
- b->ArgNames({"Effect", "Strength"});
for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
continue;
@@ -318,6 +320,8 @@
}
protected:
+ bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
auto getEffect(const State& state) const {
return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
}
@@ -333,6 +337,9 @@
if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
+ if (!hasArgs(state)) {
+ return;
+ }
int32_t id = 1;
auto effect = getEffect(state);
@@ -354,6 +361,9 @@
if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
+ if (!hasArgs(state)) {
+ return;
+ }
int32_t id = 1;
auto effect = getEffect(state);
@@ -370,6 +380,10 @@
});
BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+ if (!hasArgs(state)) {
+ return;
+ }
+
auto effect = getEffect(state);
auto strength = getStrength(state);
auto callback = []() {};
@@ -394,11 +408,13 @@
}
std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
+ b->ArgNames({"Primitive"});
+
if (supported.empty()) {
+ b->Args({static_cast<long>(-1)});
return;
}
- b->ArgNames({"Primitive"});
for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
continue;
@@ -411,6 +427,8 @@
}
protected:
+ bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
auto getPrimitive(const State& state) const {
return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
}
@@ -422,6 +440,9 @@
if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
return;
}
+ if (!hasArgs(state)) {
+ return;
+ }
hardware::vibrator::CompositeEffect effect;
effect.primitive = getPrimitive(state);